I have quite a simple JMH Benchmark, I have read left and right that using the constructor of collections should be faster than using the addAll
method.
However, my benchmark tends to prove the contrary.
Any explanation on that?
@State(Scope.Benchmark)
public static class Strings {
public String string = "String";
public List<String> strings = Arrays.asList("String123", "String456", "String789");
}
@Benchmark
@Fork(value = 5, warmups = 3)
public List<String> withStreams(Strings input) {
return Stream.concat(Stream.of(input.string), input.strings.stream())
.collect(Collectors.toList());
}
@Benchmark
@Fork(value = 5, warmups = 3)
public List<String> withoutStreamsButWithConstructor(Strings input) {
List<String> result = new ArrayList<>(input.strings);
result.add(input.string);
return result;
}
@Benchmark
@Fork(value = 5, warmups = 3)
public List<String> withoutStreams(Strings input) {
List<String> result = new ArrayList<>();
result.add(input.string);
result.addAll(input.strings);
return result;
}
PS: The example with Streams is just an experiment (in fact the actual things I wanted to be sure of)
Results
# Run complete. Total time: 00:16:31
Benchmark Mode Cnt Score Error Units
App.withStreams thrpt 100 12649053,043 ± 222716,712 ops/s
App.withoutStreams thrpt 100 50572729,531 ± 324271,706 ops/s
App.withoutStreamsButWithConstructor thrpt 100 30179733,201 ± 380273,095 ops/s
Added the following benchmark to the family
@Benchmark
@Fork(value = 5, warmups = 3)
public List<String> withoutStreamsWithAddAfter(Strings input) {
List<String> result = new ArrayList<>();
result.addAll(input.strings);
result.add(input.string);
return result;
}
And I now get
# Run complete. Total time: 00:22:00
Benchmark Mode Cnt Score Error Units
App.withStreams thrpt 100 13560464,180 ± 201012,539 ops/s
App.withoutStreams thrpt 100 47490197,224 ± 864545,886 ops/s
App.withoutStreamsButWithConstructor thrpt 100 29412182,733 ± 346228,939 ops/s
App.withoutStreamsWithAddAfter thrpt 100 31030909,677 ± 81494,995 ops/s
So the withoutStreams
is the most performant one anyway
I've tried with the following List<String>
@State(Scope.Benchmark)
public static class Strings {
public String string = "String";
public List<String> strings = Arrays.asList("String123", "String456", "String789", "StringAbc", "StringDef", "StringGhi", "StringJkl", "StringMno", "StringPqr", "StringStu");
}
And now the results are indeed better for the constructor @Benchmark
# Run complete. Total time: 00:22:01
Benchmark Mode Cnt Score Error Units
App.withStreams thrpt 100 7291967,397 ± 330614,125 ops/s
App.withoutStreams thrpt 100 23575768,665 ± 127039,282 ops/s
App.withoutStreamsButWithConstructor thrpt 100 27046342,511 ± 182227,005 ops/s
App.withoutStreamsWithAddAfter thrpt 100 17873682,945 ± 170786,259 ops/s
I suspect the reason lies in how ArrayList is implemented.
withoutStreamsButWithConstructor
creates an array of size 1, and when you call addAll
, a new array of size 4 is created, the first element is copied over and the other elements are then added.
withoutStreams
creates an array of size 10 (default capacity) and no resizing is required.
If strings
contained 10 elements, one array resizing would be required in both methods and I suspect the results would be closer.
More generally, if performance is important, sizing the list correctly with the constructor that takes an int
can make a difference.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.