Collection.toArray()vs Collection.stream()。toArray() [英] Collection.toArray() vs Collection.stream().toArray()
问题描述
考虑以下代码:
List<String> myList = Arrays.asList(1, 2, 3);
String[] myArray1 = myList.toArray(new String[myList.size()]);
String[] myArray2 = myList.stream().toArray(String[]::new);
assert Arrays.equals(myArray1, myArray2);
在我看来,使用流更加简单。
It seems to me that using a stream is much simpler.
因此,我测试了每个的速度。
Therefore, I tested the speed of each.
List<String> myList = Arrays.asList("1", "2", "3");
double start;
start = System.currentTimeMillis();
for (int i = 0; i < 10_000_000; i++) {
String[] myArray1 = myList.toArray(new String[myList.size()]);
assert myArray1.length == 3;
}
System.out.println(System.currentTimeMillis() - start);
start = System.currentTimeMillis();
for (int i = 0; i < 10_000_000; i++) {
String[] myArray2 = myList.stream().toArray(String[]::new);
assert myArray2.length == 3;
}
System.out.println(System.currentTimeMillis() - start);
结果是使用流的速度大约慢了四倍。在我的机器上,816毫秒(流)与187毫秒(没有流)。我也尝试切换时序语句(myArray1之前的myArray2),这对结果影响不大。为什么这么慢?创建一个 Stream
如此计算密集?
The result is that using the stream is about four times slower. On my machine, 816ms (stream) vs 187ms (no stream). I also tried switching the timing statements around (myArray2 prior to myArray1), which didn't effect the results much. Why is this so much slower? Is creating a Stream
so computationally intensive?
我跟着@ Holger的建议并研究了一下(肯定不够)关于JVM测试,阅读这篇文章,这篇文章,本文,并使用 JMH 。
I followed @Holger's advice and studied up a bit (surely not enough) on JVM testing, reading this post, this article, this article, and using JMH.
结果(通过JMH):
private static final List<String> myList = IntStream.range(1, 1000).mapToObj(String::valueOf).collect(Collectors.toList());
@Benchmark
public void testMethod() {
String[] myArray = myArrayList.stream().toArray(String[]::new);
}
StreamToArrayArrayListBenchmark.testMethod avgt 5 2846.346±32.500 ns / op
StreamToArrayArrayListBenchmark.testMethod avgt 5 2846.346 ± 32.500 ns/op
private static final List<String> myList = IntStream.range(1, 1000).mapToObj(String::valueOf).collect(Collectors.toList());
@Benchmark
public void testMethod() {
String[] myArray = myArrayList.toArray(new String[0]);
}
ToArrayEmptyArrayListBenchmark.testMethod avgt 5 1417.474±20.725 ns / op
ToArrayEmptyArrayListBenchmark.testMethod avgt 5 1417.474 ± 20.725 ns/op
private static final List<String> myList = IntStream.range(1, 1000).mapToObj(String::valueOf).collect(Collectors.toList());
@Benchmark
public void testMethod() {
String[] myArray = myArrayList.toArray(new String[myList.size()]);
}
ToArraySizedArrayListBenchmark.testMethod avgt 5 1853.622±178.351 ns / op
ToArraySizedArrayListBenchmark.testMethod avgt 5 1853.622 ± 178.351 ns/op
private static final List<String> myList = new LinkedList<>(IntStream.range(1, 1000).mapToObj(String::valueOf).collect(Collectors.toList()));
@Benchmark
public void testMethod() {
String[] myArray = myArrayList.stream().toArray(String[]::new);
}
StreamToArrayLinkedListBenchmark.testMethod avgt 5 4152.003±59.281 ns / op
StreamToArrayLinkedListBenchmark.testMethod avgt 5 4152.003 ± 59.281 ns/op
private static final List<String> myList = new LinkedList<>(IntStream.range(1, 1000).mapToObj(String::valueOf).collect(Collectors.toList()));
@Benchmark
public void testMethod() {
String[] myArray = myArrayList.toArray(new String[0]);
}
ToArrayEmptyLinkedListBenchmark.testMethod avgt 5 4089.550±29.880 ns / op
ToArrayEmptyLinkedListBenchmark.testMethod avgt 5 4089.550 ± 29.880 ns/op
private static final List<String> myList = new LinkedList<>(IntStream.range(1, 1000).mapToObj(String::valueOf).collect(Collectors.toList()));
@Benchmark
public void testMethod() {
String[] myArray = myArrayList.toArray(new String[myList.size()]);
}
ToArraySizedArrayListBenchmark.testMethod avgt 5 4115.557±93.964 ns / op
ToArraySizedArrayListBenchmark.testMethod avgt 5 4115.557 ± 93.964 ns/op
总结:
To summarize:
| ArrayList | LinkedList
stream | 2846 | 4152
toArray sized | 1853 | 4115
toArray empty | 1417 | 4089
使用JMH(可能天真),我'我仍然看到 ArrayList :: toArray
的速度是 Stream :: toArray
的两倍。但是,这似乎是因为 ArrayList
能够执行数组副本,正如@Andreas所指出的那样,因为当源是 LinkedList
结果大致相等。
Using JMH (possibly naively), I'm still seeing that ArrayList::toArray
is about twice as fast as Stream::toArray
. However, this does seem to be because of the ability of the ArrayList
to just do an array copy, as @Andreas pointed out because when the source is a LinkedList
the results are about equal.
了解 myList.toArray(new String [0 ])
。
推荐答案
Arrays.asList()
创建一个固定大小的 List
,它由varargs数组参数直接支持。 Javadoc甚至这样说:
Arrays.asList()
creates a fixed-size List
that is directly backed by the varargs array parameter. Javadoc even says so:
返回由指定数组支持的固定大小列表。
Returns a fixed-size list backed by the specified array.
toArray()
是一个简单的 System.arraycopy( )
。 非常快。
另一方面,当你执行 myList.stream()。toArray(String [] :: new)
,大小未知,所以 Stream.toArray()
方法必须使用流,收集所有值,然后创建数组并将值复制到数组中。这是慢,需要很多更多内存。
On the other hand, when you do myList.stream().toArray(String[]::new)
, the size is not known, so the Stream.toArray()
method has to consume the stream, collect all the values, then create the array and copy the values into the array. That is a lot slower, and requires a lot more memory.
简而言之,这是浪费资源。
In short, it's a waste of resources.
如果你想要更简单,就不要给出数组大小。它比使用Streams更快,内存更少:
If you want simpler, just don't give the array size. It is still way faster and less memory intensive than using Streams:
String[] myArray1 = myList.toArray(new String[0]);
这篇关于Collection.toArray()vs Collection.stream()。toArray()的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!