實現Java集合迭代的高效能

chszs發表於2018-07-14
版權宣告:本文為博主chszs的原創文章,未經博主允許不得轉載。 https://blog.csdn.net/chszs/article/details/81042018

實現Java集合迭代的高效能

  • 2018.7.14
  • 版權宣告:本文為博主chszs的原創文章,未經博主允許不得轉載。

一、介紹

Java開發者經常會遇到處理集合(比如ArrayList、HashSet)的情況,Java 8也提供了Lambda表示式和Streaming API來簡化集合相關的工作。在大多數應用場景下,無需考慮集合迭代的效能消耗。但是,在一些極端情況下,比如集合包含了上百萬條記錄的情況,這個時候集合迭代就需要選擇正確的姿勢,否則效能會較差。

使用JMH檢查下面每段程式碼片段的執行時間。

二、forEach vs. C Style vs. Stream API

迭代是一個非常基本的功能,所有的程式語言都有簡單的迭代語法,允許程式設計師在集合上執行迭代。Stream API可以通過Collections用非常直接的方式進行迭代。

public List<Integer> streamSingleThread(BenchMarkState state) {
    List<Integer> result = new ArrayList<>(state.testData.size());
    state.testData.stream().forEach(item -> {
        result.add(item);
    });
    return result;
}

public List<Integer> streamMultiThread(BenchMarkState state) {
    List<Integer> result = new ArrayList<>(state.testData.size());
    state.testData.stream().parallel().forEach(item -> {
        result.add(item);
    });
    return result;
}

使用forEach迴圈也非常簡單:

public List<Integer> forEach(BenchMarkState state) {
    List<Integer> result = new ArrayList<>(state.testData.size());
    for(Integer item : state.testData) {
        result.add(item);
    }
    return result;
}

C style方式的迭代其程式碼要冗長一些,但仍然非常緊湊:

public List<Integer> forCStyle(BenchMarkState state) {
    int size = state.testData.size();
    List<Integer> result = new ArrayList<>(size);
    for(int j = 0; j < size; j ++){
        result.add(state.testData.get(j));
    }
    return result;
}

以上程式碼的效能評分如下:

Benchmark Mode Cnt Score Error Units
TestLoopPerformance.forCStyle avgt 200 18.068 ± 0.074 ms/op
TestLoopPerformance.forEach avgt 200 30.566 ± 0.165 ms/op
TestLoopPerformance.streamMultiThread avgt 200 79.433 ± 0.747 ms/op
TestLoopPerformance.streamSingleThread avgt 200 37.779 ± 0.485 ms/op

對於C style方式的迭代,JVM只是簡單地增加了一個整型變數,它直接從記憶體讀值。這使它非常快。但forEach迭代則不同,根據Oracle官方文件,JVM必須把forEach轉換為迭代器併為每個資料項呼叫hasNext()。這就是為什麼forEach比C style


相關文章