dan*_*ton 2 java collections java-stream
考虑以下代码:
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);
Run Code Online (Sandbox Code Playgroud)
在我看来,使用流要简单得多。
因此,我测试了每个的速度。
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);
Run Code Online (Sandbox Code Playgroud)
结果是使用流的速度慢了大约四倍。在我的计算机上,816ms(流)对187ms(无流)。我还尝试了切换时序语句(在myArray1之前的myArray2),这对结果影响不大。为什么这么慢?创建的Stream计算量如此大吗?
我遵循@Holger的建议,对JVM测试进行了一些研究(肯定不够),阅读了这篇文章,这篇文章,这篇文章,并使用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);
}
Run Code Online (Sandbox Code Playgroud)
StreamToArrayArrayListBenchmark.testMethod平均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]);
}
Run Code Online (Sandbox Code Playgroud)
ToArrayEmptyArrayListBenchmark.testMethod平均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()]);
}
Run Code Online (Sandbox Code Playgroud)
ToArraySizedArrayListBenchmark.testMethod平均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);
}
Run Code Online (Sandbox Code Playgroud)
StreamToArrayLinkedListBenchmark.testMethod平均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]);
}
Run Code Online (Sandbox Code Playgroud)
ToArrayEmptyLinkedListBenchmark.testMethod平均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()]);
}
Run Code Online (Sandbox Code Playgroud)
ToArraySizedArrayListBenchmark.testMethod平均5 4115.557±93.964 ns / op
总结一下:
| ArrayList | LinkedList
stream | 2846 | 4152
toArray sized | 1853 | 4115
toArray empty | 1417 | 4089
Run Code Online (Sandbox Code Playgroud)
使用JMH(可能是幼稚的),我仍然看到它的ArrayList::toArray速度大约是的两倍Stream::toArray。但是,这似乎是由于ArrayList@Andreas指出的,因为它仅能进行数组复制的能力,因为当源为a时LinkedList,结果大致相等。
了解绝对是一件好事myList.toArray(new String[0])。
Arrays.asList()创建一个固定大小List的变量,该大小直接由varargs数组参数支持。Javadoc甚至说:
返回由指定数组支持的固定大小的列表。
它的实现toArray()很简单System.arraycopy()。非常快。
另一方面,当您这样做时myList.stream().toArray(String[]::new),大小是未知的,因此该Stream.toArray()方法必须使用流,收集所有值,然后创建数组并将这些值复制到数组中。这要慢得多,并且需要更多的内存。
简而言之,这是浪费资源。
如果您想要更简单,请不要给出数组大小。与使用Streams相比,它仍然更快,内存占用更少:
String[] myArray1 = myList.toArray(new String[0]);
Run Code Online (Sandbox Code Playgroud)