java8的Streams


首先看一個問題:在這個task集合中一共有多少個OPEN狀態的?計算出它們的points屬性和。在Java 8之前,要解決這個問題,則需要使用foreach循環遍歷task集合;但是在Java 8中可以利用steams解決:包括一系列元素的列表,並且支持順序和並行處理。

// Calculate total points of all active tasks using sum()
final long totalPointsOfOpenTasks = tasks
    .stream()
    .filter( task -> task.getStatus() == Status.OPEN )
    .mapToInt( Task::getPoints )
    .sum();

System.out.println( "Total points: " + totalPointsOfOpenTasks );

這里有很多知識點值得說。首先,tasks集合被轉換成steam表示;其次,在steam上的filter操作會過濾掉所有CLOSEDtask;第三,mapToInt操作基於tasks集合中的每個task實例的Task::getPoints方法將task流轉換成Integer集合;最后,通過sum方法計算總和,得出最后的結果。

在學習下一個例子之前,還需要記住一些steams(點此更多細節)的知識點。Steam之上的操作可分為中間操作和晚期操作。

中間操作會返回一個新的steam——執行一個中間操作(例如filter)並不會執行實際的過濾操作,而是創建一個新的steam,並將原steam中符合條件的元素放入新創建的steam。

晚期操作(例如forEach或者sum),會遍歷steam並得出結果或者附帶結果;在執行晚期操作之后,steam處理線已經處理完畢,就不能使用了。在幾乎所有情況下,晚期操作都是立刻對steam進行遍歷。

steam的另一個價值是創造性地支持並行處理(parallel processing)。對於上述的tasks集合,我們可以用下面的代碼計算所有task的points之和:

// Calculate total points of all tasks
final double totalPoints = tasks
   .stream()
   .parallel()
   .map( task -> task.getPoints() ) // or map( Task::getPoints ) 
   .reduce( 0, Integer::sum );

System.out.println( "Total points (all tasks): " + totalPoints );

這里我們使用parallel方法並行處理所有的task,並使用reduce方法計算最終的結果。控制台輸出如下:

對於一個集合,經常需要根據某些條件對其中的元素分組。利用steam提供的API可以很快完成這類任務,代碼如下:

// Group tasks by their status
final Map< Status, List< Task > > map = tasks
    .stream()
    .collect( Collectors.groupingBy( Task::getStatus ) );
System.out.println( map );

控制台的輸出如下:

{CLOSED=[[CLOSED, 8]], OPEN=[[OPEN, 5], [OPEN, 13]]}

最后一個關於tasks集合的例子問題是:如何計算集合中每個任務的點數在集合中所占的比重,具體處理的代碼如下:

// Calculate the weight of each tasks (as percent of total points) 
final Collection< String > result = tasks
    .stream()                                        // Stream< String >
    .mapToInt( Task::getPoints )                     // IntStream
    .asLongStream()                                  // LongStream
    .mapToDouble( points -> points / totalPoints )   // DoubleStream
    .boxed()                                         // Stream< Double >
    .mapToLong( weigth -> ( long )( weigth * 100 ) ) // LongStream
    .mapToObj( percentage -> percentage + "%" )      // Stream< String> 
    .collect( Collectors.toList() );                 // List< String > 

System.out.println( result );

控制台輸出結果如下:

[19%, 50%, 30%]

最后,正如之前所說,Steam API不僅可以作用於Java集合,傳統的IO操作(從文件或者網絡一行一行得讀取數據)可以受益於steam處理,這里有一個小例子:

final Path path = new File( filename ).toPath();
try( Stream< String > lines = Files.lines( path, StandardCharsets.UTF_8 ) ) {
    lines.onClose( () -> System.out.println("Done!") ).forEach( System.out::println );
}

Stream的方法onClose() 返回一個等價的有額外句柄的Stream,當Stream的close()方法被調用的時候這個句柄會被執行。Stream API、Lambda表達式還有接口默認方法和靜態方法支持的方法引用,是Java 8對軟件開發的現代范式的響應。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM