Avoid resizing of fixed-size HashSet/LinkedHashSet variants

Add helpers to CollectionUtils for building HashSets and LinkedHashSets
that can hold an expected number of elements without needing to
resize/rehash.

Closes gh-32291
This commit is contained in:
Patrick Strawderman
2024-02-18 14:05:31 -08:00
committed by Sam Brannen
parent 6383a0d7ca
commit e1a32d4ba9
47 changed files with 114 additions and 75 deletions

View File

@@ -18,7 +18,6 @@ package org.springframework.core.convert.support;
import java.lang.reflect.Array;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.Optional;
import java.util.Set;
@@ -26,6 +25,7 @@ import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.ConditionalGenericConverter;
import org.springframework.lang.Nullable;
import org.springframework.util.CollectionUtils;
/**
* Convert an Object to {@code java.util.Optional<T>} if necessary using the
@@ -48,7 +48,7 @@ final class ObjectToOptionalConverter implements ConditionalGenericConverter {
@Override
public Set<ConvertiblePair> getConvertibleTypes() {
Set<ConvertiblePair> convertibleTypes = new LinkedHashSet<>(4);
Set<ConvertiblePair> convertibleTypes = CollectionUtils.newLinkedHashSet(3);
convertibleTypes.add(new ConvertiblePair(Collection.class, Optional.class));
convertibleTypes.add(new ConvertiblePair(Object[].class, Optional.class));
convertibleTypes.add(new ConvertiblePair(Object.class, Optional.class));

View File

@@ -24,6 +24,7 @@ import java.util.List;
import java.util.Set;
import org.springframework.lang.Nullable;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
/**
@@ -89,7 +90,7 @@ public class CompositePropertySource extends EnumerablePropertySource<Object> {
namesList.add(names);
total += names.length;
}
Set<String> allNames = new LinkedHashSet<>(total);
Set<String> allNames = CollectionUtils.newLinkedHashSet(total);
namesList.forEach(names -> Collections.addAll(allNames, names));
return StringUtils.toStringArray(allNames);
}

View File

@@ -30,7 +30,6 @@ import java.nio.channels.WritableByteChannel;
import java.nio.file.OpenOption;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.Executor;
@@ -54,6 +53,7 @@ import reactor.util.context.Context;
import org.springframework.core.io.Resource;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
/**
* Utility class for working with {@link DataBuffer DataBuffers}.
@@ -382,7 +382,7 @@ public abstract class DataBufferUtils {
private static Set<OpenOption> checkWriteOptions(OpenOption[] options) {
int length = options.length;
Set<OpenOption> result = new HashSet<>(length + 3);
Set<OpenOption> result = CollectionUtils.newHashSet(length > 0 ? length : 2);
if (length == 0) {
result.add(StandardOpenOption.CREATE);
result.add(StandardOpenOption.TRUNCATE_EXISTING);

View File

@@ -22,8 +22,10 @@ import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
@@ -85,7 +87,7 @@ public abstract class CollectionUtils {
* @see #newLinkedHashMap(int)
*/
public static <K, V> HashMap<K, V> newHashMap(int expectedSize) {
return new HashMap<>(computeMapInitialCapacity(expectedSize), DEFAULT_LOAD_FACTOR);
return new HashMap<>(computeInitialCapacity(expectedSize), DEFAULT_LOAD_FACTOR);
}
/**
@@ -102,10 +104,34 @@ public abstract class CollectionUtils {
* @see #newHashMap(int)
*/
public static <K, V> LinkedHashMap<K, V> newLinkedHashMap(int expectedSize) {
return new LinkedHashMap<>(computeMapInitialCapacity(expectedSize), DEFAULT_LOAD_FACTOR);
return new LinkedHashMap<>(computeInitialCapacity(expectedSize), DEFAULT_LOAD_FACTOR);
}
private static int computeMapInitialCapacity(int expectedSize) {
/**
* Instantiate a new {@link HashSet} with an initial capacity
* that can accommodate the specified number of elements without
* any immediate resize/rehash operations to be expected.
* @param expectedSize the expected number of elements (with a corresponding
* capacity to be derived so that no resize/rehash operations are needed)
* @see #newLinkedHashSet(int)
*/
public static <E> HashSet<E> newHashSet(int expectedSize) {
return new HashSet<>(computeInitialCapacity(expectedSize), DEFAULT_LOAD_FACTOR);
}
/**
* Instantiate a new {@link LinkedHashSet} with an initial capacity
* that can accommodate the specified number of elements without
* any immediate resize/rehash operations to be expected.
* @param expectedSize the expected number of elements (with a corresponding
* capacity to be derived so that no resize/rehash operations are needed)
* @see #newHashSet(int)
*/
public static <E> LinkedHashSet<E> newLinkedHashSet(int expectedSize) {
return new LinkedHashSet<>(computeInitialCapacity(expectedSize), DEFAULT_LOAD_FACTOR);
}
private static int computeInitialCapacity(int expectedSize) {
return (int) Math.ceil(expectedSize / (double) DEFAULT_LOAD_FACTOR);
}