[英]Difference between Collection.toArray() and Collection.toArray(Object obj[])
[英]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);
在我看來,使用流要簡單得多。
因此,我測試了每個的速度。
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 測試進行了一些研究(肯定還不夠),閱讀了這篇文章、 這篇文章、 這篇文章,並使用了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
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])
絕對是件好事。
Arrays.asList()
創建一個由可變參數數組參數直接支持的固定大小List
。 Javadoc 甚至這樣說:
返回由指定數組支持的固定大小列表。
它對toArray()
是一個簡單的System.arraycopy()
。 非常快。
另一方面,當您執行myList.stream().toArray(String[]::new)
,大小未知,因此Stream.toArray()
方法必須消耗流,收集所有值,然后創建數組並將值復制到數組中。 那會慢很多,並且需要更多的內存。
簡而言之,這是一種資源浪費。
如果你想要更簡單,就不要給出數組大小。 與使用 Streams 相比,它仍然更快且內存占用更少:
String[] myArray1 = myList.toArray(new String[0]);
在幕后,流比普通數組復雜得多。 編譯器會變得更好,但目前,順序 for 循環應該比流操作更快。
本文介紹了用於實現流的流管道的一些背景知識。 它可以幫助理解其背后的復雜性。
流的優點是代碼可以更清晰,更容易並行化。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.