Support for SequencedCollection/Set/Map (on JDK 21)
Includes consistent support for MultiValueMap. Closes gh-30239
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2022 the original author or authors.
|
* Copyright 2002-2023 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -73,11 +73,13 @@ public final class CollectionFactory {
|
|||||||
private static final Set<Class<?>> approximableMapTypes = Set.of(
|
private static final Set<Class<?>> approximableMapTypes = Set.of(
|
||||||
// Standard map interfaces
|
// Standard map interfaces
|
||||||
Map.class,
|
Map.class,
|
||||||
|
MultiValueMap.class,
|
||||||
SortedMap.class,
|
SortedMap.class,
|
||||||
NavigableMap.class,
|
NavigableMap.class,
|
||||||
// Common concrete map classes
|
// Common concrete map classes
|
||||||
HashMap.class,
|
HashMap.class,
|
||||||
LinkedHashMap.class,
|
LinkedHashMap.class,
|
||||||
|
LinkedMultiValueMap.class,
|
||||||
TreeMap.class,
|
TreeMap.class,
|
||||||
EnumMap.class);
|
EnumMap.class);
|
||||||
|
|
||||||
@@ -93,7 +95,9 @@ public final class CollectionFactory {
|
|||||||
* @return {@code true} if the type is <em>approximable</em>
|
* @return {@code true} if the type is <em>approximable</em>
|
||||||
*/
|
*/
|
||||||
public static boolean isApproximableCollectionType(@Nullable Class<?> collectionType) {
|
public static boolean isApproximableCollectionType(@Nullable Class<?> collectionType) {
|
||||||
return (collectionType != null && approximableCollectionTypes.contains(collectionType));
|
return (collectionType != null && (approximableCollectionTypes.contains(collectionType) ||
|
||||||
|
collectionType.getName().equals("java.util.SequencedSet") ||
|
||||||
|
collectionType.getName().equals("java.util.SequencedCollection")));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -118,13 +122,7 @@ public final class CollectionFactory {
|
|||||||
*/
|
*/
|
||||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||||
public static <E> Collection<E> createApproximateCollection(@Nullable Object collection, int capacity) {
|
public static <E> Collection<E> createApproximateCollection(@Nullable Object collection, int capacity) {
|
||||||
if (collection instanceof LinkedList) {
|
if (collection instanceof EnumSet enumSet) {
|
||||||
return new LinkedList<>();
|
|
||||||
}
|
|
||||||
else if (collection instanceof List) {
|
|
||||||
return new ArrayList<>(capacity);
|
|
||||||
}
|
|
||||||
else if (collection instanceof EnumSet enumSet) {
|
|
||||||
Collection<E> copy = EnumSet.copyOf(enumSet);
|
Collection<E> copy = EnumSet.copyOf(enumSet);
|
||||||
copy.clear();
|
copy.clear();
|
||||||
return copy;
|
return copy;
|
||||||
@@ -132,6 +130,12 @@ public final class CollectionFactory {
|
|||||||
else if (collection instanceof SortedSet sortedSet) {
|
else if (collection instanceof SortedSet sortedSet) {
|
||||||
return new TreeSet<>(sortedSet.comparator());
|
return new TreeSet<>(sortedSet.comparator());
|
||||||
}
|
}
|
||||||
|
else if (collection instanceof LinkedList) {
|
||||||
|
return new LinkedList<>();
|
||||||
|
}
|
||||||
|
else if (collection instanceof List) {
|
||||||
|
return new ArrayList<>(capacity);
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
return new LinkedHashSet<>(capacity);
|
return new LinkedHashSet<>(capacity);
|
||||||
}
|
}
|
||||||
@@ -178,7 +182,9 @@ public final class CollectionFactory {
|
|||||||
public static <E> Collection<E> createCollection(Class<?> collectionType, @Nullable Class<?> elementType, int capacity) {
|
public static <E> Collection<E> createCollection(Class<?> collectionType, @Nullable Class<?> elementType, int capacity) {
|
||||||
Assert.notNull(collectionType, "Collection type must not be null");
|
Assert.notNull(collectionType, "Collection type must not be null");
|
||||||
if (LinkedHashSet.class == collectionType || HashSet.class == collectionType ||
|
if (LinkedHashSet.class == collectionType || HashSet.class == collectionType ||
|
||||||
Set.class == collectionType || Collection.class == collectionType) {
|
Set.class == collectionType || Collection.class == collectionType ||
|
||||||
|
collectionType.getName().equals("java.util.SequencedSet") ||
|
||||||
|
collectionType.getName().equals("java.util.SequencedCollection")) {
|
||||||
return new LinkedHashSet<>(capacity);
|
return new LinkedHashSet<>(capacity);
|
||||||
}
|
}
|
||||||
else if (ArrayList.class == collectionType || List.class == collectionType) {
|
else if (ArrayList.class == collectionType || List.class == collectionType) {
|
||||||
@@ -187,8 +193,8 @@ public final class CollectionFactory {
|
|||||||
else if (LinkedList.class == collectionType) {
|
else if (LinkedList.class == collectionType) {
|
||||||
return new LinkedList<>();
|
return new LinkedList<>();
|
||||||
}
|
}
|
||||||
else if (TreeSet.class == collectionType || NavigableSet.class == collectionType
|
else if (TreeSet.class == collectionType || NavigableSet.class == collectionType ||
|
||||||
|| SortedSet.class == collectionType) {
|
SortedSet.class == collectionType) {
|
||||||
return new TreeSet<>();
|
return new TreeSet<>();
|
||||||
}
|
}
|
||||||
else if (EnumSet.class.isAssignableFrom(collectionType)) {
|
else if (EnumSet.class.isAssignableFrom(collectionType)) {
|
||||||
@@ -216,7 +222,8 @@ public final class CollectionFactory {
|
|||||||
* @return {@code true} if the type is <em>approximable</em>
|
* @return {@code true} if the type is <em>approximable</em>
|
||||||
*/
|
*/
|
||||||
public static boolean isApproximableMapType(@Nullable Class<?> mapType) {
|
public static boolean isApproximableMapType(@Nullable Class<?> mapType) {
|
||||||
return (mapType != null && approximableMapTypes.contains(mapType));
|
return (mapType != null && (approximableMapTypes.contains(mapType) ||
|
||||||
|
mapType.getName().equals("java.util.SequencedMap")));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -246,6 +253,9 @@ public final class CollectionFactory {
|
|||||||
else if (map instanceof SortedMap sortedMap) {
|
else if (map instanceof SortedMap sortedMap) {
|
||||||
return new TreeMap<>(sortedMap.comparator());
|
return new TreeMap<>(sortedMap.comparator());
|
||||||
}
|
}
|
||||||
|
else if (map instanceof MultiValueMap) {
|
||||||
|
return new LinkedMultiValueMap(capacity);
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
return new LinkedHashMap<>(capacity);
|
return new LinkedHashMap<>(capacity);
|
||||||
}
|
}
|
||||||
@@ -292,26 +302,22 @@ public final class CollectionFactory {
|
|||||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||||
public static <K, V> Map<K, V> createMap(Class<?> mapType, @Nullable Class<?> keyType, int capacity) {
|
public static <K, V> Map<K, V> createMap(Class<?> mapType, @Nullable Class<?> keyType, int capacity) {
|
||||||
Assert.notNull(mapType, "Map type must not be null");
|
Assert.notNull(mapType, "Map type must not be null");
|
||||||
if (mapType.isInterface()) {
|
if (LinkedHashMap.class == mapType || HashMap.class == mapType || Map.class == mapType ||
|
||||||
if (Map.class == mapType) {
|
mapType.getName().equals("java.util.SequencedMap")) {
|
||||||
return new LinkedHashMap<>(capacity);
|
return new LinkedHashMap<>(capacity);
|
||||||
}
|
}
|
||||||
else if (SortedMap.class == mapType || NavigableMap.class == mapType) {
|
else if (LinkedMultiValueMap.class == mapType || MultiValueMap.class == mapType) {
|
||||||
return new TreeMap<>();
|
return new LinkedMultiValueMap();
|
||||||
}
|
}
|
||||||
else if (MultiValueMap.class == mapType) {
|
else if (TreeMap.class == mapType || SortedMap.class == mapType || NavigableMap.class == mapType) {
|
||||||
return new LinkedMultiValueMap();
|
return new TreeMap<>();
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw new IllegalArgumentException("Unsupported Map interface: " + mapType.getName());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (EnumMap.class == mapType) {
|
else if (EnumMap.class == mapType) {
|
||||||
Assert.notNull(keyType, "Cannot create EnumMap for unknown key type");
|
Assert.notNull(keyType, "Cannot create EnumMap for unknown key type");
|
||||||
return new EnumMap(asEnumType(keyType));
|
return new EnumMap(asEnumType(keyType));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (!Map.class.isAssignableFrom(mapType)) {
|
if (mapType.isInterface() || !Map.class.isAssignableFrom(mapType)) {
|
||||||
throw new IllegalArgumentException("Unsupported Map type: " + mapType.getName());
|
throw new IllegalArgumentException("Unsupported Map type: " + mapType.getName());
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2022 the original author or authors.
|
* Copyright 2002-2023 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -209,21 +209,25 @@ class CollectionFactoryTests {
|
|||||||
@Test
|
@Test
|
||||||
void createsCollectionsCorrectly() {
|
void createsCollectionsCorrectly() {
|
||||||
// interfaces
|
// interfaces
|
||||||
assertThat(createCollection(List.class, 0)).isInstanceOf(ArrayList.class);
|
testCollection(List.class, ArrayList.class);
|
||||||
assertThat(createCollection(Set.class, 0)).isInstanceOf(LinkedHashSet.class);
|
testCollection(Set.class, LinkedHashSet.class);
|
||||||
assertThat(createCollection(Collection.class, 0)).isInstanceOf(LinkedHashSet.class);
|
testCollection(Collection.class, LinkedHashSet.class);
|
||||||
assertThat(createCollection(SortedSet.class, 0)).isInstanceOf(TreeSet.class);
|
// on JDK 21: testCollection(SequencedSet.class, LinkedHashSet.class);
|
||||||
assertThat(createCollection(NavigableSet.class, 0)).isInstanceOf(TreeSet.class);
|
// on JDK 21: testCollection(SequencedCollection.class, LinkedHashSet.class);
|
||||||
|
testCollection(SortedSet.class, TreeSet.class);
|
||||||
assertThat(createCollection(List.class, String.class, 0)).isInstanceOf(ArrayList.class);
|
testCollection(NavigableSet.class, TreeSet.class);
|
||||||
assertThat(createCollection(Set.class, String.class, 0)).isInstanceOf(LinkedHashSet.class);
|
|
||||||
assertThat(createCollection(Collection.class, String.class, 0)).isInstanceOf(LinkedHashSet.class);
|
|
||||||
assertThat(createCollection(SortedSet.class, String.class, 0)).isInstanceOf(TreeSet.class);
|
|
||||||
assertThat(createCollection(NavigableSet.class, String.class, 0)).isInstanceOf(TreeSet.class);
|
|
||||||
|
|
||||||
// concrete types
|
// concrete types
|
||||||
assertThat(createCollection(HashSet.class, 0)).isInstanceOf(HashSet.class);
|
testCollection(ArrayList.class, ArrayList.class);
|
||||||
assertThat(createCollection(HashSet.class, String.class, 0)).isInstanceOf(HashSet.class);
|
testCollection(HashSet.class, LinkedHashSet.class);
|
||||||
|
testCollection(LinkedHashSet.class, LinkedHashSet.class);
|
||||||
|
testCollection(TreeSet.class, TreeSet.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testCollection(Class<?> collectionType, Class<?> resultType) {
|
||||||
|
assertThat(CollectionFactory.isApproximableCollectionType(collectionType)).isTrue();
|
||||||
|
assertThat(createCollection(collectionType, 0)).isInstanceOf(resultType);
|
||||||
|
assertThat(createCollection(collectionType, String.class, 0)).isInstanceOf(resultType);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -258,20 +262,23 @@ class CollectionFactoryTests {
|
|||||||
@Test
|
@Test
|
||||||
void createsMapsCorrectly() {
|
void createsMapsCorrectly() {
|
||||||
// interfaces
|
// interfaces
|
||||||
assertThat(createMap(Map.class, 0)).isInstanceOf(LinkedHashMap.class);
|
testMap(Map.class, LinkedHashMap.class);
|
||||||
assertThat(createMap(SortedMap.class, 0)).isInstanceOf(TreeMap.class);
|
// on JDK 21: testMap(SequencedMap.class, LinkedHashMap.class);
|
||||||
assertThat(createMap(NavigableMap.class, 0)).isInstanceOf(TreeMap.class);
|
testMap(SortedMap.class, TreeMap.class);
|
||||||
assertThat(createMap(MultiValueMap.class, 0)).isInstanceOf(LinkedMultiValueMap.class);
|
testMap(NavigableMap.class, TreeMap.class);
|
||||||
|
testMap(MultiValueMap.class, LinkedMultiValueMap.class);
|
||||||
assertThat(createMap(Map.class, String.class, 0)).isInstanceOf(LinkedHashMap.class);
|
|
||||||
assertThat(createMap(SortedMap.class, String.class, 0)).isInstanceOf(TreeMap.class);
|
|
||||||
assertThat(createMap(NavigableMap.class, String.class, 0)).isInstanceOf(TreeMap.class);
|
|
||||||
assertThat(createMap(MultiValueMap.class, String.class, 0)).isInstanceOf(LinkedMultiValueMap.class);
|
|
||||||
|
|
||||||
// concrete types
|
// concrete types
|
||||||
assertThat(createMap(HashMap.class, 0)).isInstanceOf(HashMap.class);
|
testMap(HashMap.class, LinkedHashMap.class);
|
||||||
|
testMap(LinkedHashMap.class, LinkedHashMap.class);
|
||||||
|
testMap(TreeMap.class, TreeMap.class);
|
||||||
|
testMap(LinkedMultiValueMap.class, LinkedMultiValueMap.class);
|
||||||
|
}
|
||||||
|
|
||||||
assertThat(createMap(HashMap.class, String.class, 0)).isInstanceOf(HashMap.class);
|
private void testMap(Class<?> mapType, Class<?> resultType) {
|
||||||
|
assertThat(CollectionFactory.isApproximableMapType(mapType)).isTrue();
|
||||||
|
assertThat(createMap(mapType, 0)).isInstanceOf(resultType);
|
||||||
|
assertThat(createMap(mapType, String.class, 0)).isInstanceOf(resultType);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
Reference in New Issue
Block a user