请考虑以下代码:
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);
在我看来,使用流更简单。
因此,我测试了每个的速度。
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
计算密集型?
我遵循了@ Holger的建议并对JVM测试进行了一些研究(当然不够),阅读this post,this article,this article和使用JMH。< / p>
结果(通过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
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
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
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
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
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
总结:
| ArrayList | LinkedList
stream | 2846 | 4152
toArray sized | 1853 | 4115
toArray empty | 1417 | 4089
使用JMH(可能天真地),我仍然看到ArrayList::toArray
的速度是Stream::toArray
的两倍。但是,这似乎是因为ArrayList
能够执行数组副本,正如@Andreas指出的那样,因为当源是LinkedList
时,结果大致相等。
了解myList.toArray(new String[0])
非常好。
答案 0 :(得分:6)
Arrays.asList()
创建一个固定大小的List
,由varargs数组参数直接支持。 Javadoc甚至这么说:
返回由指定数组支持的固定大小的列表。
toArray()
的实施很简单System.arraycopy()
。 非常快。
另一方面,当您执行myList.stream().toArray(String[]::new)
时,大小未知,因此Stream.toArray()
方法必须使用流,收集所有值,然后创建数组并将值复制到数组中。这是慢,需要很多更多内存。
简而言之,这是浪费资源。
如果您想要更简单,请不要给出数组大小。它比使用Streams更快,内存更少:
String[] myArray1 = myList.toArray(new String[0]);
答案 1 :(得分:0)
在引擎盖下,流比普通阵列复杂得多。编译器会变得更好,但是目前,循环顺序应该比流操作更快。
This article有一些关于流管道的背景知识,用于实现流。它可以帮助理解它背后的复杂性。
流的优点是代码可以更清晰,并且更容易并行化。