From 800b13492b627d02da5d3591ee9ae9c04833da81 Mon Sep 17 00:00:00 2001 From: James Yuzawa Date: Wed, 22 Mar 2023 06:04:54 -0400 Subject: [PATCH] Optimize some iterations in BodyExtractor and BodyInserter This commit turns some stream-based iterations back into simpler enhanced for loops. For simple use cases like these, where the stream API is merely used to map/filter + collect to a List, a for loop is more efficient. This is especially true for small collections like the ones we deal with in BodyInserters/BodyExtractors here (in the order of 50ns/op vs 5ns/op). These cases are also simple enough that they don't lose in readability after the conversion. Closes gh-30136 --- .../reactive/AbstractClientHttpRequest.java | 6 ++-- .../web/reactive/function/BodyExtractors.java | 35 +++++++++---------- .../web/reactive/function/BodyInserters.java | 26 +++++++------- 3 files changed, 35 insertions(+), 32 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/http/client/reactive/AbstractClientHttpRequest.java b/spring-web/src/main/java/org/springframework/http/client/reactive/AbstractClientHttpRequest.java index 14346902f2b..144e0d058da 100644 --- a/spring-web/src/main/java/org/springframework/http/client/reactive/AbstractClientHttpRequest.java +++ b/spring-web/src/main/java/org/springframework/http/client/reactive/AbstractClientHttpRequest.java @@ -147,8 +147,10 @@ public abstract class AbstractClientHttpRequest implements ClientHttpRequest { this.commitActions.add(writeAction); } - List> actions = this.commitActions.stream() - .map(Supplier::get).toList(); + List> actions = new ArrayList<>(this.commitActions.size()); + for (Supplier> commitAction : this.commitActions) { + actions.add(commitAction.get()); + } return Flux.concat(actions).then(); } diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/function/BodyExtractors.java b/spring-webflux/src/main/java/org/springframework/web/reactive/function/BodyExtractors.java index 4baa24b4f14..0b4d4167558 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/function/BodyExtractors.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/BodyExtractors.java @@ -194,18 +194,16 @@ public abstract class BodyExtractors { MediaType contentType = Optional.ofNullable(message.getHeaders().getContentType()) .orElse(MediaType.APPLICATION_OCTET_STREAM); - return context.messageReaders().stream() - .filter(reader -> reader.canRead(elementType, contentType)) - .findFirst() - .map(BodyExtractors::cast) - .map(readerFunction) - .orElseGet(() -> { - List mediaTypes = context.messageReaders().stream() - .flatMap(reader -> reader.getReadableMediaTypes(elementType).stream()) - .toList(); - return errorFunction.apply( - new UnsupportedMediaTypeException(contentType, mediaTypes, elementType)); - }); + for (HttpMessageReader messageReader : context.messageReaders()) { + if (messageReader.canRead(elementType, contentType)) { + return readerFunction.apply(cast(messageReader)); + } + } + List mediaTypes = context.messageReaders().stream() + .flatMap(reader -> reader.getReadableMediaTypes(elementType).stream()) + .toList(); + return errorFunction.apply( + new UnsupportedMediaTypeException(contentType, mediaTypes, elementType)); } private static Mono readToMono(ReactiveHttpInputMessage message, BodyExtractor.Context context, @@ -245,12 +243,13 @@ public abstract class BodyExtractors { private static HttpMessageReader findReader( ResolvableType elementType, MediaType mediaType, BodyExtractor.Context context) { - return context.messageReaders().stream() - .filter(messageReader -> messageReader.canRead(elementType, mediaType)) - .findFirst() - .map(BodyExtractors::cast) - .orElseThrow(() -> new IllegalStateException( - "No HttpMessageReader for \"" + mediaType + "\" and \"" + elementType + "\"")); + for (HttpMessageReader messageReader : context.messageReaders()) { + if (messageReader.canRead(elementType, mediaType)) { + return cast(messageReader); + } + } + throw new IllegalStateException( + "No HttpMessageReader for \"" + mediaType + "\" and \"" + elementType + "\""); } @SuppressWarnings("unchecked") diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/function/BodyInserters.java b/spring-webflux/src/main/java/org/springframework/web/reactive/function/BodyInserters.java index 7822ff79ebe..06935a32897 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/function/BodyInserters.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/BodyInserters.java @@ -373,12 +373,13 @@ public abstract class BodyInserters { publisher = Mono.just(body); } MediaType mediaType = outputMessage.getHeaders().getContentType(); - return context.messageWriters().stream() - .filter(messageWriter -> messageWriter.canWrite(bodyType, mediaType)) - .findFirst() - .map(BodyInserters::cast) - .map(writer -> write(publisher, bodyType, mediaType, outputMessage, context, writer)) - .orElseGet(() -> Mono.error(unsupportedError(bodyType, context, mediaType))); + for (HttpMessageWriter messageWriter : context.messageWriters()) { + if (messageWriter.canWrite(bodyType, mediaType)) { + HttpMessageWriter typedMessageWriter = cast(messageWriter); + return write(publisher, bodyType, mediaType, outputMessage, context, typedMessageWriter); + } + } + return Mono.error(unsupportedError(bodyType, context, mediaType)); } private static UnsupportedMediaTypeException unsupportedError(ResolvableType bodyType, @@ -406,12 +407,13 @@ public abstract class BodyInserters { private static HttpMessageWriter findWriter( BodyInserter.Context context, ResolvableType elementType, @Nullable MediaType mediaType) { - return context.messageWriters().stream() - .filter(messageWriter -> messageWriter.canWrite(elementType, mediaType)) - .findFirst() - .map(BodyInserters::cast) - .orElseThrow(() -> new IllegalStateException( - "No HttpMessageWriter for \"" + mediaType + "\" and \"" + elementType + "\"")); + for (HttpMessageWriter messageWriter : context.messageWriters()) { + if (messageWriter.canWrite(elementType, mediaType)) { + return cast(messageWriter); + } + } + throw new IllegalStateException( + "No HttpMessageWriter for \"" + mediaType + "\" and \"" + elementType + "\""); } @SuppressWarnings("unchecked")