簡體   English   中英

Collection.toArray() 與 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);

在我看來,使用流要簡單得多。

因此,我測試了每個的速度。

 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM