From 66d527c23b9e367ec6f98a72c5ab120bb93cbccb Mon Sep 17 00:00:00 2001 From: bbaker Date: Thu, 24 Apr 2025 14:24:25 +1000 Subject: [PATCH 1/2] Removing some fo the Optional.map() and .stream() for performance reasons. --- src/main/java/graphql/GraphqlErrorHelper.java | 7 ++- .../java/graphql/execution/Execution.java | 8 +-- .../graphql/execution/ExecutionContext.java | 7 ++- .../graphql/execution/ExecutionStrategy.java | 8 +-- .../execution/ValuesResolverConversion.java | 21 ++++---- .../execution/ValuesResolverLegacy.java | 8 +-- .../incremental/DeferredExecutionSupport.java | 18 ++++--- ...spatchStrategyWithDeferAlwaysDispatch.java | 12 +++-- .../DelayedIncrementalPartialResultImpl.java | 12 +++-- .../IncrementalExecutionResultImpl.java | 11 ++--- .../incremental/IncrementalPayload.java | 6 ++- .../fetching/LambdaFetchingSupport.java | 23 ++++++--- .../FieldVisibilitySchemaTransformation.java | 7 +-- src/main/java/graphql/util/FpKit.java | 49 +++++++++++-------- 14 files changed, 114 insertions(+), 83 deletions(-) diff --git a/src/main/java/graphql/GraphqlErrorHelper.java b/src/main/java/graphql/GraphqlErrorHelper.java index 35a20d03f7..dd1bd1cd0b 100644 --- a/src/main/java/graphql/GraphqlErrorHelper.java +++ b/src/main/java/graphql/GraphqlErrorHelper.java @@ -77,8 +77,11 @@ public static Object location(SourceLocation location) { } static List fromSpecification(List> specificationMaps) { - return specificationMaps.stream() - .map(GraphqlErrorHelper::fromSpecification).collect(Collectors.toList()); + List list = new ArrayList<>(); + for (Map specificationMap : specificationMaps) { + list.add(fromSpecification(specificationMap)); + } + return list; } static GraphQLError fromSpecification(Map specificationMap) { diff --git a/src/main/java/graphql/execution/Execution.java b/src/main/java/graphql/execution/Execution.java index a784325f3d..e932347132 100644 --- a/src/main/java/graphql/execution/Execution.java +++ b/src/main/java/graphql/execution/Execution.java @@ -180,9 +180,7 @@ private CompletableFuture executeOperation(ExecutionContext exe MergedSelectionSet fields = fieldCollector.collectFields( collectorParameters, operationDefinition.getSelectionSet(), - Optional.ofNullable(executionContext.getGraphQLContext()) - .map(graphqlContext -> graphqlContext.getBoolean(ExperimentalApi.ENABLE_INCREMENTAL_SUPPORT)) - .orElse(false) + executionContext.hasIncrementalSupport() ); ResultPath path = ResultPath.rootPath(); @@ -255,9 +253,7 @@ private DataLoaderDispatchStrategy createDataLoaderDispatchStrategy(ExecutionCon return DataLoaderDispatchStrategy.NO_OP; } if (!executionContext.isSubscriptionOperation()) { - boolean deferEnabled = Optional.ofNullable(executionContext.getGraphQLContext()) - .map(graphqlContext -> graphqlContext.getBoolean(ExperimentalApi.ENABLE_INCREMENTAL_SUPPORT)) - .orElse(false); + boolean deferEnabled = executionContext.hasIncrementalSupport(); // Dedicated strategy for defer support, for safety purposes. return deferEnabled ? diff --git a/src/main/java/graphql/execution/ExecutionContext.java b/src/main/java/graphql/execution/ExecutionContext.java index a53ae621e1..22e2d7b638 100644 --- a/src/main/java/graphql/execution/ExecutionContext.java +++ b/src/main/java/graphql/execution/ExecutionContext.java @@ -360,9 +360,14 @@ public ResultNodesInfo getResultNodesInfo() { return resultNodesInfo; } + @Internal + public boolean hasIncrementalSupport() { + GraphQLContext graphqlContext = getGraphQLContext(); + return graphqlContext != null && graphqlContext.getBoolean(ExperimentalApi.ENABLE_INCREMENTAL_SUPPORT); + } + @Internal public EngineRunningState getEngineRunningState() { return engineRunningState; } - } diff --git a/src/main/java/graphql/execution/ExecutionStrategy.java b/src/main/java/graphql/execution/ExecutionStrategy.java index f9370d1e91..8dc66a625c 100644 --- a/src/main/java/graphql/execution/ExecutionStrategy.java +++ b/src/main/java/graphql/execution/ExecutionStrategy.java @@ -300,9 +300,7 @@ private static Map buildFieldValueMap(List fieldNames, L DeferredExecutionSupport createDeferredExecutionSupport(ExecutionContext executionContext, ExecutionStrategyParameters parameters) { MergedSelectionSet fields = parameters.getFields(); - return Optional.ofNullable(executionContext.getGraphQLContext()) - .map(graphqlContext -> graphqlContext.getBoolean(ExperimentalApi.ENABLE_INCREMENTAL_SUPPORT)) - .orElse(false) ? + return executionContext.hasIncrementalSupport() ? new DeferredExecutionSupport.DeferredExecutionSupportImpl( fields, parameters, @@ -928,9 +926,7 @@ protected Object completeValueForObject(ExecutionContext executionContext, Execu MergedSelectionSet subFields = fieldCollector.collectFields( collectorParameters, parameters.getField(), - Optional.ofNullable(executionContext.getGraphQLContext()) - .map(graphqlContext -> graphqlContext.getBoolean(ExperimentalApi.ENABLE_INCREMENTAL_SUPPORT)) - .orElse(false) + executionContext.hasIncrementalSupport() ); ExecutionStepInfo newExecutionStepInfo = executionStepInfo.changeTypeWithPreservedNonNull(resolvedObjectType); diff --git a/src/main/java/graphql/execution/ValuesResolverConversion.java b/src/main/java/graphql/execution/ValuesResolverConversion.java index c53eeb64e8..416ff1c642 100644 --- a/src/main/java/graphql/execution/ValuesResolverConversion.java +++ b/src/main/java/graphql/execution/ValuesResolverConversion.java @@ -602,16 +602,17 @@ private static List externalValueToInternalValueForList( ) throws CoercingParseValueException, NonNullableValueCoercedAsNullException { GraphQLInputType wrappedType = (GraphQLInputType) graphQLList.getWrappedType(); - return FpKit.toListOrSingletonList(value) - .stream() - .map(val -> externalValueToInternalValueImpl( - inputInterceptor, - fieldVisibility, - wrappedType, - val, - graphqlContext, - locale)) - .collect(toList()); + List list = new ArrayList<>(); + for (Object val : FpKit.toListOrSingletonList(value)) { + list.add(externalValueToInternalValueImpl( + inputInterceptor, + fieldVisibility, + wrappedType, + val, + graphqlContext, + locale)); + } + return list; } /** diff --git a/src/main/java/graphql/execution/ValuesResolverLegacy.java b/src/main/java/graphql/execution/ValuesResolverLegacy.java index d5e58f4656..bab1aeee25 100644 --- a/src/main/java/graphql/execution/ValuesResolverLegacy.java +++ b/src/main/java/graphql/execution/ValuesResolverLegacy.java @@ -133,10 +133,10 @@ private static Value handleNumberLegacy(String stringValue) { private static Value handleListLegacy(Object value, GraphQLList type, GraphQLContext graphqlContext, Locale locale) { GraphQLType itemType = type.getWrappedType(); if (FpKit.isIterable(value)) { - List valuesNodes = FpKit.toListOrSingletonList(value) - .stream() - .map(item -> valueToLiteralLegacy(item, itemType, graphqlContext, locale)) - .collect(toList()); + List valuesNodes = new ArrayList<>(); + for (Object item : FpKit.toListOrSingletonList(value)) { + valuesNodes.add(valueToLiteralLegacy(item, itemType, graphqlContext, locale)); + } return ArrayValue.newArrayValue().values(valuesNodes).build(); } return valueToLiteralLegacy(value, itemType, graphqlContext, locale); diff --git a/src/main/java/graphql/execution/incremental/DeferredExecutionSupport.java b/src/main/java/graphql/execution/incremental/DeferredExecutionSupport.java index 3b8e7efe8a..db6b6b19b1 100644 --- a/src/main/java/graphql/execution/incremental/DeferredExecutionSupport.java +++ b/src/main/java/graphql/execution/incremental/DeferredExecutionSupport.java @@ -15,8 +15,10 @@ import graphql.incremental.IncrementalPayload; import graphql.util.FpKit; +import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -24,7 +26,6 @@ import java.util.concurrent.CompletableFuture; import java.util.function.BiFunction; import java.util.function.Supplier; -import java.util.stream.Collectors; /** * The purpose of this class hierarchy is to encapsulate most of the logic for deferring field execution, thus @@ -106,9 +107,11 @@ public List getNonDeferredFieldNames(List allFieldNames) { @Override public Set> createCalls(ExecutionStrategyParameters executionStrategyParameters) { - return deferredExecutionToFields.keySet().stream() - .map(deferredExecution -> this.createDeferredFragmentCall(deferredExecution, executionStrategyParameters)) - .collect(Collectors.toSet()); + Set> set = new HashSet<>(); + for (DeferredExecution deferredExecution : deferredExecutionToFields.keySet()) { + set.add(this.createDeferredFragmentCall(deferredExecution, executionStrategyParameters)); + } + return set; } private DeferredFragmentCall createDeferredFragmentCall(DeferredExecution deferredExecution, ExecutionStrategyParameters executionStrategyParameters) { @@ -116,9 +119,10 @@ private DeferredFragmentCall createDeferredFragmentCall(DeferredExecution deferr List mergedFields = deferredExecutionToFields.get(deferredExecution); - List>> calls = mergedFields.stream() - .map(currentField -> this.createResultSupplier(currentField, deferredCallContext, executionStrategyParameters)) - .collect(Collectors.toList()); + List>> calls = new ArrayList<>(); + for (MergedField currentField : mergedFields) { + calls.add(this.createResultSupplier(currentField, deferredCallContext, executionStrategyParameters)); + } return new DeferredFragmentCall( deferredExecution.getLabel(), diff --git a/src/main/java/graphql/execution/instrumentation/dataloader/PerLevelDataLoaderDispatchStrategyWithDeferAlwaysDispatch.java b/src/main/java/graphql/execution/instrumentation/dataloader/PerLevelDataLoaderDispatchStrategyWithDeferAlwaysDispatch.java index 26c847b754..1a9ec7c826 100644 --- a/src/main/java/graphql/execution/instrumentation/dataloader/PerLevelDataLoaderDispatchStrategyWithDeferAlwaysDispatch.java +++ b/src/main/java/graphql/execution/instrumentation/dataloader/PerLevelDataLoaderDispatchStrategyWithDeferAlwaysDispatch.java @@ -6,6 +6,7 @@ import graphql.execution.ExecutionContext; import graphql.execution.ExecutionStrategyParameters; import graphql.execution.FieldValueInfo; +import graphql.execution.MergedField; import graphql.schema.DataFetcher; import graphql.util.LockKit; import org.dataloader.DataLoaderRegistry; @@ -195,10 +196,13 @@ public void fieldFetched(ExecutionContext executionContext, } private void increaseCallCounts(int curLevel, ExecutionStrategyParameters parameters) { - int nonDeferredFieldCount = (int) parameters.getFields().getSubFieldsList().stream() - .filter(field -> !field.isDeferred()) - .count(); - + int count = 0; + for (MergedField field : parameters.getFields().getSubFieldsList()) { + if (!field.isDeferred()) { + count++; + } + } + int nonDeferredFieldCount = count; callStack.lock.runLocked(() -> { callStack.increaseExpectedFetchCount(curLevel, nonDeferredFieldCount); callStack.increaseHappenedStrategyCalls(curLevel); diff --git a/src/main/java/graphql/incremental/DelayedIncrementalPartialResultImpl.java b/src/main/java/graphql/incremental/DelayedIncrementalPartialResultImpl.java index 461d658e7d..5ba8ef7e4f 100644 --- a/src/main/java/graphql/incremental/DelayedIncrementalPartialResultImpl.java +++ b/src/main/java/graphql/incremental/DelayedIncrementalPartialResultImpl.java @@ -2,11 +2,11 @@ import graphql.ExperimentalApi; +import java.util.ArrayList; import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import java.util.stream.Collectors; @ExperimentalApi public class DelayedIncrementalPartialResultImpl implements DelayedIncrementalPartialResult { @@ -44,10 +44,12 @@ public Map toSpecification() { result.put("extensions", extensions); } - if(incrementalItems != null) { - result.put("incremental", incrementalItems.stream() - .map(IncrementalPayload::toSpecification) - .collect(Collectors.toList())); + if (incrementalItems != null) { + List> list = new ArrayList<>(); + for (IncrementalPayload incrementalItem : incrementalItems) { + list.add(incrementalItem.toSpecification()); + } + result.put("incremental", list); } return result; diff --git a/src/main/java/graphql/incremental/IncrementalExecutionResultImpl.java b/src/main/java/graphql/incremental/IncrementalExecutionResultImpl.java index 8bd8e62a01..2f9470b949 100644 --- a/src/main/java/graphql/incremental/IncrementalExecutionResultImpl.java +++ b/src/main/java/graphql/incremental/IncrementalExecutionResultImpl.java @@ -11,7 +11,6 @@ import java.util.List; import java.util.Map; import java.util.function.Consumer; -import java.util.stream.Collectors; @ExperimentalApi public class IncrementalExecutionResultImpl extends ExecutionResultImpl implements IncrementalExecutionResult { @@ -66,11 +65,11 @@ public Map toSpecification() { map.put("hasNext", hasNext); if (this.incremental != null) { - map.put("incremental", - this.incremental.stream() - .map(IncrementalPayload::toSpecification) - .collect(Collectors.toCollection(LinkedList::new)) - ); + LinkedList> linkedList = new LinkedList<>(); + for (IncrementalPayload incrementalPayload : this.incremental) { + linkedList.add(incrementalPayload.toSpecification()); + } + map.put("incremental", linkedList); } return map; diff --git a/src/main/java/graphql/incremental/IncrementalPayload.java b/src/main/java/graphql/incremental/IncrementalPayload.java index efeba39290..a0a8fc509e 100644 --- a/src/main/java/graphql/incremental/IncrementalPayload.java +++ b/src/main/java/graphql/incremental/IncrementalPayload.java @@ -80,7 +80,11 @@ public Map toSpecification() { } protected Object errorsToSpec(List errors) { - return errors.stream().map(GraphQLError::toSpecification).collect(toList()); + List> list = new ArrayList<>(); + for (GraphQLError error : errors) { + list.add(error.toSpecification()); + } + return list; } public int hashCode() { diff --git a/src/main/java/graphql/schema/fetching/LambdaFetchingSupport.java b/src/main/java/graphql/schema/fetching/LambdaFetchingSupport.java index 51ced4aba2..8d5707939b 100644 --- a/src/main/java/graphql/schema/fetching/LambdaFetchingSupport.java +++ b/src/main/java/graphql/schema/fetching/LambdaFetchingSupport.java @@ -69,9 +69,12 @@ private static Method getCandidateMethod(Class sourceClass, String propertyNa Predicate getterPredicate = method -> isGetterNamed(method) && propertyName.equals(mkPropertyNameGetter(method)); List allGetterMethods = findMethodsForProperty(sourceClass, getterPredicate); - List pojoGetterMethods = allGetterMethods.stream() - .filter(LambdaFetchingSupport::isPossiblePojoMethod) - .collect(toList()); + List pojoGetterMethods = new ArrayList<>(); + for (Method allGetterMethod : allGetterMethods) { + if (isPossiblePojoMethod(allGetterMethod)) { + pojoGetterMethods.add(allGetterMethod); + } + } if (!pojoGetterMethods.isEmpty()) { Method method = pojoGetterMethods.get(0); if (isBooleanGetter(method)) { @@ -97,7 +100,13 @@ private static Method checkForSingleParameterPeer(Method candidateMethod, List methods) { // we prefer isX() over getX() if both happen to be present - Optional isMethod = methods.stream().filter(method -> method.getName().startsWith("is")).findFirst(); + Optional isMethod = Optional.empty(); + for (Method method : methods) { + if (method.getName().startsWith("is")) { + isMethod = Optional.of(method); + break; + } + } return isMethod.orElse(methods.get(0)); } @@ -121,9 +130,9 @@ private static List findMethodsForProperty(Class sourceClass, Predica currentClass = currentClass.getSuperclass(); } - return methods.stream() - .sorted(Comparator.comparing(Method::getName)) - .collect(toList()); + List list = new ArrayList<>(methods); + list.sort(Comparator.comparing(Method::getName)); + return list; } private static boolean isPossiblePojoMethod(Method method) { diff --git a/src/main/java/graphql/schema/transform/FieldVisibilitySchemaTransformation.java b/src/main/java/graphql/schema/transform/FieldVisibilitySchemaTransformation.java index 303b81b9c8..325854129c 100644 --- a/src/main/java/graphql/schema/transform/FieldVisibilitySchemaTransformation.java +++ b/src/main/java/graphql/schema/transform/FieldVisibilitySchemaTransformation.java @@ -62,9 +62,10 @@ public final GraphQLSchema apply(GraphQLSchema schema) { Set markedForRemovalTypes = new HashSet<>(); // query, mutation, and subscription types should not be removed - final Set protectedTypeNames = getOperationTypes(schema).stream() - .map(GraphQLObjectType::getName) - .collect(Collectors.toSet()); + final Set protectedTypeNames = new HashSet<>(); + for (GraphQLObjectType graphQLObjectType : getOperationTypes(schema)) { + protectedTypeNames.add(graphQLObjectType.getName()); + } beforeTransformationHook.run(); diff --git a/src/main/java/graphql/util/FpKit.java b/src/main/java/graphql/util/FpKit.java index a538450ad3..a7ef6480c1 100644 --- a/src/main/java/graphql/util/FpKit.java +++ b/src/main/java/graphql/util/FpKit.java @@ -36,12 +36,11 @@ public class FpKit { // // From a list of named things, get a map of them by name, merging them according to the merge function public static Map getByName(List namedObjects, Function nameFn, BinaryOperator mergeFunc) { - return namedObjects.stream().collect(Collectors.toMap( - nameFn, - identity(), - mergeFunc, - LinkedHashMap::new) - ); + Map map = new LinkedHashMap<>(); + for (T namedObject : namedObjects) { + map.merge(nameFn.apply(namedObject), namedObject, mergeFunc); + } + return map; } // normal groupingBy but with LinkedHashMap @@ -60,12 +59,11 @@ public static Map> groupingBy(Stream str } public static Map groupingByUniqueKey(Collection list, Function keyFunction) { - return list.stream().collect(Collectors.toMap( - keyFunction, - identity(), - throwingMerger(), - LinkedHashMap::new) - ); + Map map = new LinkedHashMap<>(); + for (T t : list) { + map.merge(keyFunction.apply(t), t, throwingMerger()); + } + return map; } public static Map groupingByUniqueKey(Stream stream, Function keyFunction) { @@ -241,7 +239,11 @@ public static List valuesToList(Map map) { } public static List mapEntries(Map map, BiFunction function) { - return map.entrySet().stream().map(entry -> function.apply(entry.getKey(), entry.getValue())).collect(Collectors.toList()); + List list = new ArrayList<>(); + for (Map.Entry entry : map.entrySet()) { + list.add(function.apply(entry.getKey(), entry.getValue())); + } + return list; } @@ -272,10 +274,12 @@ public static List flatList(Collection> listLists) { } public static Optional findOne(Collection list, Predicate filter) { - return list - .stream() - .filter(filter) - .findFirst(); + for (T t : list) { + if (filter.test(t)) { + return Optional.of(t); + } + } + return Optional.empty(); } public static T findOneOrNull(List list, Predicate filter) { @@ -292,10 +296,13 @@ public static int findIndex(List list, Predicate filter) { } public static List filterList(Collection list, Predicate filter) { - return list - .stream() - .filter(filter) - .collect(Collectors.toList()); + List result = new ArrayList<>(); + for (T t : list) { + if (filter.test(t)) { + result.add(t); + } + } + return result; } public static Set filterSet(Collection input, Predicate filter) { From 35eb00ee0719d84252997398ed93ceb963bb6755 Mon Sep 17 00:00:00 2001 From: bbaker Date: Fri, 9 May 2025 16:20:39 +1000 Subject: [PATCH 2/2] Added FpKit.arrayListSizedTo() --- src/main/java/graphql/GraphqlErrorHelper.java | 4 ++-- .../execution/ValuesResolverConversion.java | 5 ++-- .../execution/ValuesResolverLegacy.java | 5 ++-- .../incremental/DeferredExecutionSupport.java | 8 +++---- .../DelayedIncrementalPartialResultImpl.java | 4 ++-- .../incremental/IncrementalPayload.java | 13 ++++++---- .../fetching/LambdaFetchingSupport.java | 5 ++-- src/main/java/graphql/util/FpKit.java | 24 +++++++++++++++---- src/test/groovy/graphql/util/FpKitTest.groovy | 7 ++++++ 9 files changed, 51 insertions(+), 24 deletions(-) diff --git a/src/main/java/graphql/GraphqlErrorHelper.java b/src/main/java/graphql/GraphqlErrorHelper.java index dd1bd1cd0b..391b223d92 100644 --- a/src/main/java/graphql/GraphqlErrorHelper.java +++ b/src/main/java/graphql/GraphqlErrorHelper.java @@ -1,13 +1,13 @@ package graphql; import graphql.language.SourceLocation; +import graphql.util.FpKit; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Objects; -import java.util.stream.Collectors; import static graphql.collect.ImmutableKit.mapAndDropNulls; @@ -77,7 +77,7 @@ public static Object location(SourceLocation location) { } static List fromSpecification(List> specificationMaps) { - List list = new ArrayList<>(); + List list = FpKit.arrayListSizedTo(specificationMaps); for (Map specificationMap : specificationMaps) { list.add(fromSpecification(specificationMap)); } diff --git a/src/main/java/graphql/execution/ValuesResolverConversion.java b/src/main/java/graphql/execution/ValuesResolverConversion.java index 416ff1c642..29e2587ab8 100644 --- a/src/main/java/graphql/execution/ValuesResolverConversion.java +++ b/src/main/java/graphql/execution/ValuesResolverConversion.java @@ -602,8 +602,9 @@ private static List externalValueToInternalValueForList( ) throws CoercingParseValueException, NonNullableValueCoercedAsNullException { GraphQLInputType wrappedType = (GraphQLInputType) graphQLList.getWrappedType(); - List list = new ArrayList<>(); - for (Object val : FpKit.toListOrSingletonList(value)) { + List listOrSingletonList = FpKit.toListOrSingletonList(value); + List list = FpKit.arrayListSizedTo(listOrSingletonList); + for (Object val : listOrSingletonList) { list.add(externalValueToInternalValueImpl( inputInterceptor, fieldVisibility, diff --git a/src/main/java/graphql/execution/ValuesResolverLegacy.java b/src/main/java/graphql/execution/ValuesResolverLegacy.java index bab1aeee25..d98a744f7c 100644 --- a/src/main/java/graphql/execution/ValuesResolverLegacy.java +++ b/src/main/java/graphql/execution/ValuesResolverLegacy.java @@ -133,8 +133,9 @@ private static Value handleNumberLegacy(String stringValue) { private static Value handleListLegacy(Object value, GraphQLList type, GraphQLContext graphqlContext, Locale locale) { GraphQLType itemType = type.getWrappedType(); if (FpKit.isIterable(value)) { - List valuesNodes = new ArrayList<>(); - for (Object item : FpKit.toListOrSingletonList(value)) { + List listOrSingletonList = FpKit.toListOrSingletonList(value); + List valuesNodes = FpKit.arrayListSizedTo(listOrSingletonList); + for (Object item : listOrSingletonList) { valuesNodes.add(valueToLiteralLegacy(item, itemType, graphqlContext, locale)); } return ArrayValue.newArrayValue().values(valuesNodes).build(); diff --git a/src/main/java/graphql/execution/incremental/DeferredExecutionSupport.java b/src/main/java/graphql/execution/incremental/DeferredExecutionSupport.java index db6b6b19b1..0ae917ee74 100644 --- a/src/main/java/graphql/execution/incremental/DeferredExecutionSupport.java +++ b/src/main/java/graphql/execution/incremental/DeferredExecutionSupport.java @@ -15,7 +15,6 @@ import graphql.incremental.IncrementalPayload; import graphql.util.FpKit; -import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; @@ -107,8 +106,9 @@ public List getNonDeferredFieldNames(List allFieldNames) { @Override public Set> createCalls(ExecutionStrategyParameters executionStrategyParameters) { - Set> set = new HashSet<>(); - for (DeferredExecution deferredExecution : deferredExecutionToFields.keySet()) { + ImmutableSet deferredExecutions = deferredExecutionToFields.keySet(); + Set> set = new HashSet<>(deferredExecutions.size()); + for (DeferredExecution deferredExecution : deferredExecutions) { set.add(this.createDeferredFragmentCall(deferredExecution, executionStrategyParameters)); } return set; @@ -119,7 +119,7 @@ private DeferredFragmentCall createDeferredFragmentCall(DeferredExecution deferr List mergedFields = deferredExecutionToFields.get(deferredExecution); - List>> calls = new ArrayList<>(); + List>> calls = FpKit.arrayListSizedTo(mergedFields); for (MergedField currentField : mergedFields) { calls.add(this.createResultSupplier(currentField, deferredCallContext, executionStrategyParameters)); } diff --git a/src/main/java/graphql/incremental/DelayedIncrementalPartialResultImpl.java b/src/main/java/graphql/incremental/DelayedIncrementalPartialResultImpl.java index 5ba8ef7e4f..3412d77298 100644 --- a/src/main/java/graphql/incremental/DelayedIncrementalPartialResultImpl.java +++ b/src/main/java/graphql/incremental/DelayedIncrementalPartialResultImpl.java @@ -1,8 +1,8 @@ package graphql.incremental; import graphql.ExperimentalApi; +import graphql.util.FpKit; -import java.util.ArrayList; import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; @@ -45,7 +45,7 @@ public Map toSpecification() { } if (incrementalItems != null) { - List> list = new ArrayList<>(); + List> list = FpKit.arrayListSizedTo(incrementalItems); for (IncrementalPayload incrementalItem : incrementalItems) { list.add(incrementalItem.toSpecification()); } diff --git a/src/main/java/graphql/incremental/IncrementalPayload.java b/src/main/java/graphql/incremental/IncrementalPayload.java index a0a8fc509e..742d857c89 100644 --- a/src/main/java/graphql/incremental/IncrementalPayload.java +++ b/src/main/java/graphql/incremental/IncrementalPayload.java @@ -3,6 +3,7 @@ import graphql.ExperimentalApi; import graphql.GraphQLError; import graphql.execution.ResultPath; +import graphql.util.FpKit; import org.jspecify.annotations.Nullable; import java.util.ArrayList; @@ -11,8 +12,6 @@ import java.util.Map; import java.util.Objects; -import static java.util.stream.Collectors.toList; - /** * Represents a payload that can be resolved after the initial response. */ @@ -80,7 +79,7 @@ public Map toSpecification() { } protected Object errorsToSpec(List errors) { - List> list = new ArrayList<>(); + List> list = FpKit.arrayListSizedTo(errors); for (GraphQLError error : errors) { list.add(error.toSpecification()); } @@ -92,8 +91,12 @@ public int hashCode() { } public boolean equals(Object obj) { - if (this == obj) return true; - if (obj == null || getClass() != obj.getClass()) return false; + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } IncrementalPayload that = (IncrementalPayload) obj; return Objects.equals(path, that.path) && Objects.equals(label, that.label) && diff --git a/src/main/java/graphql/schema/fetching/LambdaFetchingSupport.java b/src/main/java/graphql/schema/fetching/LambdaFetchingSupport.java index 8d5707939b..5ff38f5756 100644 --- a/src/main/java/graphql/schema/fetching/LambdaFetchingSupport.java +++ b/src/main/java/graphql/schema/fetching/LambdaFetchingSupport.java @@ -2,6 +2,7 @@ import graphql.Internal; import graphql.VisibleForTesting; +import graphql.util.FpKit; import java.lang.invoke.CallSite; import java.lang.invoke.LambdaMetafactory; @@ -17,8 +18,6 @@ import java.util.function.Function; import java.util.function.Predicate; -import static java.util.stream.Collectors.toList; - @Internal public class LambdaFetchingSupport { @@ -69,7 +68,7 @@ private static Method getCandidateMethod(Class sourceClass, String propertyNa Predicate getterPredicate = method -> isGetterNamed(method) && propertyName.equals(mkPropertyNameGetter(method)); List allGetterMethods = findMethodsForProperty(sourceClass, getterPredicate); - List pojoGetterMethods = new ArrayList<>(); + List pojoGetterMethods = FpKit.arrayListSizedTo(allGetterMethods); for (Method allGetterMethod : allGetterMethods) { if (isPossiblePojoMethod(allGetterMethod)) { pojoGetterMethods.add(allGetterMethod); diff --git a/src/main/java/graphql/util/FpKit.java b/src/main/java/graphql/util/FpKit.java index a7ef6480c1..2aa04b60f3 100644 --- a/src/main/java/graphql/util/FpKit.java +++ b/src/main/java/graphql/util/FpKit.java @@ -5,6 +5,7 @@ import com.google.common.collect.ImmutableSet; import com.google.common.collect.Sets; import graphql.Internal; +import org.jspecify.annotations.NonNull; import java.lang.reflect.Array; import java.util.ArrayList; @@ -117,6 +118,19 @@ public static Collection toCollection(Object iterableResult) { return list; } + /** + * Creates an {@link ArrayList} sized appropriately to the collection, typically for copying + * + * @param collection the collection of a certain size + * @param to two + * + * @return a new {@link ArrayList} initially sized to the same as the collection + */ + public static @NonNull List arrayListSizedTo(@NonNull Collection collection) { + return new ArrayList<>(collection.size()); + } + + /** * Converts a value into a list if it's really a collection or array of things * else it turns it into a singleton list containing that one value @@ -239,8 +253,9 @@ public static List valuesToList(Map map) { } public static List mapEntries(Map map, BiFunction function) { - List list = new ArrayList<>(); - for (Map.Entry entry : map.entrySet()) { + Set> entries = map.entrySet(); + List list = arrayListSizedTo(entries); + for (Map.Entry entry : entries) { list.add(function.apply(entry.getKey(), entry.getValue())); } return list; @@ -296,7 +311,7 @@ public static int findIndex(List list, Predicate filter) { } public static List filterList(Collection list, Predicate filter) { - List result = new ArrayList<>(); + List result = arrayListSizedTo(list); for (T t : list) { if (filter.test(t)) { result.add(t); @@ -359,9 +374,10 @@ public static Supplier interThreadMemoize(Supplier delegate) { /** * Faster set intersection. * - * @param for two + * @param for two * @param set1 first set * @param set2 second set + * * @return intersection set */ public static Set intersection(Set set1, Set set2) { diff --git a/src/test/groovy/graphql/util/FpKitTest.groovy b/src/test/groovy/graphql/util/FpKitTest.groovy index 455e9b57a1..6891f98d53 100644 --- a/src/test/groovy/graphql/util/FpKitTest.groovy +++ b/src/test/groovy/graphql/util/FpKitTest.groovy @@ -134,4 +134,11 @@ class FpKitTest extends Specification { then: intersection.isEmpty() } + + def "test sized allocation"() { + when: + def newArrayList = FpKit.arrayListSizedTo(["a", "b", "c"]) + then: + newArrayList instanceof ArrayList + } }