Skip to content

Commit 800b134

Browse files
authored
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
1 parent 4e896c8 commit 800b134

File tree

3 files changed

+35
-32
lines changed

3 files changed

+35
-32
lines changed

spring-web/src/main/java/org/springframework/http/client/reactive/AbstractClientHttpRequest.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -147,8 +147,10 @@ protected Mono<Void> doCommit(@Nullable Supplier<? extends Publisher<Void>> writ
147147
this.commitActions.add(writeAction);
148148
}
149149

150-
List<? extends Publisher<Void>> actions = this.commitActions.stream()
151-
.map(Supplier::get).toList();
150+
List<Publisher<Void>> actions = new ArrayList<>(this.commitActions.size());
151+
for (Supplier<? extends Publisher<Void>> commitAction : this.commitActions) {
152+
actions.add(commitAction.get());
153+
}
152154

153155
return Flux.concat(actions).then();
154156
}

spring-webflux/src/main/java/org/springframework/web/reactive/function/BodyExtractors.java

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -194,18 +194,16 @@ private static <T, S extends Publisher<T>> S readWithMessageReaders(
194194
MediaType contentType = Optional.ofNullable(message.getHeaders().getContentType())
195195
.orElse(MediaType.APPLICATION_OCTET_STREAM);
196196

197-
return context.messageReaders().stream()
198-
.filter(reader -> reader.canRead(elementType, contentType))
199-
.findFirst()
200-
.map(BodyExtractors::<T>cast)
201-
.map(readerFunction)
202-
.orElseGet(() -> {
203-
List<MediaType> mediaTypes = context.messageReaders().stream()
204-
.flatMap(reader -> reader.getReadableMediaTypes(elementType).stream())
205-
.toList();
206-
return errorFunction.apply(
207-
new UnsupportedMediaTypeException(contentType, mediaTypes, elementType));
208-
});
197+
for (HttpMessageReader<?> messageReader : context.messageReaders()) {
198+
if (messageReader.canRead(elementType, contentType)) {
199+
return readerFunction.apply(cast(messageReader));
200+
}
201+
}
202+
List<MediaType> mediaTypes = context.messageReaders().stream()
203+
.flatMap(reader -> reader.getReadableMediaTypes(elementType).stream())
204+
.toList();
205+
return errorFunction.apply(
206+
new UnsupportedMediaTypeException(contentType, mediaTypes, elementType));
209207
}
210208

211209
private static <T> Mono<T> readToMono(ReactiveHttpInputMessage message, BodyExtractor.Context context,
@@ -245,12 +243,13 @@ private static <T> Flux<T> unsupportedErrorHandler(
245243
private static <T> HttpMessageReader<T> findReader(
246244
ResolvableType elementType, MediaType mediaType, BodyExtractor.Context context) {
247245

248-
return context.messageReaders().stream()
249-
.filter(messageReader -> messageReader.canRead(elementType, mediaType))
250-
.findFirst()
251-
.map(BodyExtractors::<T>cast)
252-
.orElseThrow(() -> new IllegalStateException(
253-
"No HttpMessageReader for \"" + mediaType + "\" and \"" + elementType + "\""));
246+
for (HttpMessageReader<?> messageReader : context.messageReaders()) {
247+
if (messageReader.canRead(elementType, mediaType)) {
248+
return cast(messageReader);
249+
}
250+
}
251+
throw new IllegalStateException(
252+
"No HttpMessageReader for \"" + mediaType + "\" and \"" + elementType + "\"");
254253
}
255254

256255
@SuppressWarnings("unchecked")

spring-webflux/src/main/java/org/springframework/web/reactive/function/BodyInserters.java

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -373,12 +373,13 @@ else if (adapter != null) {
373373
publisher = Mono.just(body);
374374
}
375375
MediaType mediaType = outputMessage.getHeaders().getContentType();
376-
return context.messageWriters().stream()
377-
.filter(messageWriter -> messageWriter.canWrite(bodyType, mediaType))
378-
.findFirst()
379-
.map(BodyInserters::cast)
380-
.map(writer -> write(publisher, bodyType, mediaType, outputMessage, context, writer))
381-
.orElseGet(() -> Mono.error(unsupportedError(bodyType, context, mediaType)));
376+
for (HttpMessageWriter<?> messageWriter : context.messageWriters()) {
377+
if (messageWriter.canWrite(bodyType, mediaType)) {
378+
HttpMessageWriter<Object> typedMessageWriter = cast(messageWriter);
379+
return write(publisher, bodyType, mediaType, outputMessage, context, typedMessageWriter);
380+
}
381+
}
382+
return Mono.error(unsupportedError(bodyType, context, mediaType));
382383
}
383384

384385
private static UnsupportedMediaTypeException unsupportedError(ResolvableType bodyType,
@@ -406,12 +407,13 @@ private static <T> Mono<Void> write(Publisher<? extends T> input, ResolvableType
406407
private static <T> HttpMessageWriter<T> findWriter(
407408
BodyInserter.Context context, ResolvableType elementType, @Nullable MediaType mediaType) {
408409

409-
return context.messageWriters().stream()
410-
.filter(messageWriter -> messageWriter.canWrite(elementType, mediaType))
411-
.findFirst()
412-
.map(BodyInserters::<T>cast)
413-
.orElseThrow(() -> new IllegalStateException(
414-
"No HttpMessageWriter for \"" + mediaType + "\" and \"" + elementType + "\""));
410+
for (HttpMessageWriter<?> messageWriter : context.messageWriters()) {
411+
if (messageWriter.canWrite(elementType, mediaType)) {
412+
return cast(messageWriter);
413+
}
414+
}
415+
throw new IllegalStateException(
416+
"No HttpMessageWriter for \"" + mediaType + "\" and \"" + elementType + "\"");
415417
}
416418

417419
@SuppressWarnings("unchecked")

0 commit comments

Comments
 (0)