聲明:本篇轉(zhuǎn)自《【譯】Java 8的新特性—終極版》中的 Streams 相關(guān)部分。這里只是為了方便查閱學習耸三。其他詳細信息乱陡,可參見原文或官方文檔。
Stream
新增的Stream API(java.util.stream)將生成環(huán)境的函數(shù)式編程引入了Java庫中仪壮。這是目前為止最大的一次對Java庫的完善憨颠,以便開發(fā)者能夠?qū)懗龈佑行А⒏雍啙嵑途o湊的代碼积锅。
Stream API極大得簡化了集合操作(后面我們會看到不止是集合)爽彤,首先看下這個叫Task的類:
public class Streams {
private enum Status { OPEN, CLOSED };
private static final class Task {
private final Status status;
private final Integer points;
Task( final Status status, final Integer points ) {
this.status = status; this.points = points;
}
public Integer getPoints() {
return points;
}
public Status getStatus() {
return status;
}
@Override public String toString() {
return String.format( "[%s, %d]", status, points );
}
}
}
Task類有一個分數(shù)(或偽復(fù)雜度)的概念,另外還有兩種狀態(tài):OPEN或者CLOSED》Ψ校現(xiàn)在假設(shè)有一個task集合:
final Collection< Task > tasks = Arrays.asList(
new Task( Status.OPEN, 5 ),
new Task( Status.OPEN, 13 ),
new Task( Status.CLOSED, 8 )
);
首先看一個問題:在這個task集合中一共有多少個OPEN狀態(tài)的點淫茵?在Java 8之前爪瓜,要解決這個問題蹬跃,則需要使用foreach循環(huán)遍歷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 );
運行這個方法的控制臺輸出是:
Total points: 18
這里有很多知識點值得說丹喻。首先,tasks集合被轉(zhuǎn)換成stream表示翁都;其次碍论,在stream上的filter操作會過濾掉所有CLOSED的task;第三柄慰,mapToInt操作基于每個task實例的Task::getPoints方法將task流轉(zhuǎn)換成Integer集合鳍悠;最后,通過sum方法計算總和坐搔,得出最后的結(jié)果藏研。
在學習下一個例子之前,還需要記住一些streams(點此更多細節(jié))的知識點概行。Stream之上的操作可分為中間操作和晚期操作蠢挡。
中間操作會返回一個新的steam——執(zhí)行一個中間操作(例如filter)并不會執(zhí)行實際的過濾操作,而是創(chuàng)建一個新的stream凳忙,并將原stream中符合條件的元素放入新創(chuàng)建的stream业踏。
晚期操作(例如forEach或者sum),會遍歷stream并得出結(jié)果或者附帶結(jié)果涧卵;在執(zhí)行晚期操作之后勤家,stream處理線已經(jīng)處理完畢,就不能使用了柳恐。在幾乎所有情況下却紧,晚期操作都是立刻對steam進行遍歷。
stream的另一個價值是創(chuàng)造性地支持并行處理(parallel processing)胎撤。對于上述的tasks集合晓殊,我們可以用下面的代碼計算所有任務(wù)的點數(shù)之和:
// 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方法計算最終的結(jié)果伤提∥装常控制臺輸出如下:
Total points(all tasks): 26.0
對于一個集合,經(jīng)常需要根據(jù)某些條件對其中的元素分組肿男。利用steam提供的API可以很快完成這類任務(wù)介汹,代碼如下:
// 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]]}
最后一個關(guān)于tasks集合的例子問題是:如何計算集合中每個任務(wù)的點數(shù)在集合中所占的比重,具體處理的代碼如下:
// 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 );
控制臺輸出結(jié)果如下:
[19%, 50%, 30%]
最后舶沛,正如之前所說嘹承,Steam API不僅可以作用于Java集合,傳統(tǒng)的IO操作(從文件或者網(wǎng)絡(luò)一行一行得讀取數(shù)據(jù))可以受益于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()方法被調(diào)用的時候這個句柄會被執(zhí)行。Stream API、Lambda表達式還有接口默認方法和靜態(tài)方法支持的方法引用骤竹,是Java 8對軟件開發(fā)的現(xiàn)代范式的響應(yīng)帝牡。