stream流式編程
前言
JDK的發(fā)行版本都已經(jīng)衍生至19了,這個從8就引入的Stream流應當是屬于Java程序員基操了。然而最近面試遇到開發(fā)經(jīng)驗3年的工程師,對它似乎不是很熟悉,讓我大吃一驚。本文通過常用的一些簡單的例子把它以最小的時間成本給大家說明白。
面試大全:guan zhu weixin gongzhonghao : Java面試教程 ?;貜停簈qq?
一、楔子
在使用集合的時候迭代往往是使用的最多的,對比是否使用stream的代碼實現(xiàn),
????public?int?calcSum(List<Integer>?list)?{
????????int?sum?=?0;
????????for?(int?i?=?0;?i?<?list.size();?i++)?{
????????????sum?+=?list.get(i);
????????}
????????return?sum;
????}
????
????public?int?calcSumLambda(List<Integer>?list)?{
????????return?list.stream().mapToInt(x?->?x).sum();
????}
第一次看到這樣的寫法時,可能會認為這樣的代碼可讀性不高,但當你熟悉之后,你會改變對它的看法。
二、如何創(chuàng)建流
想要使用Stream,首先需要創(chuàng)建一個流,最常見的是直接調(diào)用集合的java.util.Collection#stream
方法
????private?void?createByCollection()?{
????????List<Integer>?list?=?new?ArrayList<>();
????????Stream<Integer>?stream?=?list.stream();
????}
當然通過數(shù)組同樣能夠創(chuàng)建
????private?void?createByArrays()?{
????????Integer[]?array1?=?{1,?2,?3};
????????Integer[]?array2?=?new?Integer[]?{1,?2,?3};
????????Stream<Integer>?stream1?=?Stream.of(array1);
????????Stream<Integer>?stream2?=?Arrays.stream(array1);
????}
三、流操作的分類
Stream流操作共分為兩個大類:惰性求值、及早求值
????/**
?????*?通過Stream流過濾元素返回新的集合
?????*?
?????*?@param?list?待過濾的集合
?????*?@return?新的集合
?????*/
????private?List<Integer>?filterByStream(List<Integer>?list)?{
????????return?list.stream().filter(number?->?number?>?1).collect(Collectors.toList());
????}
Stream操作時,先調(diào)用了filter方法傳入了一個Lambda表達式代表過濾規(guī)則,后調(diào)用了collect方法表示將流轉(zhuǎn)換為List集合。
我們不需要去記哪些方法是惰性求值,如果方法的返回值是Stream那么它代表的就是惰性求值。如果返回另外一個值或空,那么它代表的就是及早求值。
這些流操作定義之后,在程序中是怎么調(diào)用的定義的lambda表達式的?
例如在java.util.stream.ReferencePipeline抽象類中有對Stream接口collect的實現(xiàn),方法由final修飾,不再支持重寫。
????@Override
????@SuppressWarnings("unchecked")
????public?final?<R,?A>?R?collect(Collector<??super?P_OUT,?A,?R>?collector)?{
????????A?container;
????????if?(isParallel()
????????????????&&?(collector.characteristics().contains(Collector.Characteristics.CONCURRENT))
????????????????&&?(!isOrdered()?||?collector.characteristics().contains(Collector.Characteristics.UNORDERED)))?{
????????????container?=?collector.supplier().get();
????????????BiConsumer<A,???super?P_OUT>?accumulator?=?collector.accumulator();
????????????forEach(u?->?accumulator.accept(container,?u));
????????}
????????else?{
????????????container?=?evaluate(ReduceOps.makeRef(collector));
????????}
????????return?collector.characteristics().contains(Collector.Characteristics.IDENTITY_FINISH)
?????????????????(R)?container
???????????????:?collector.finisher().apply(container);
????}
四、常用基操
map
映射,x->y,轉(zhuǎn)換數(shù)據(jù)類型
????/**
?????*?通過Stream?map操作將小寫的字符串集合轉(zhuǎn)換為大寫
?????*?
?????*?@param?list?小寫字符串集合
?????*?@return?大寫字符串集合
?????*/
????public?List<String>?toUpperByStreamMap(List<String>?list)?{
????????//?return?list.stream().map(String::toUpperCase).collect(Collectors.toList());
????????return?list.stream().map(string?->?string.toUpperCase()).collect(Collectors.toList());
????}
filter
過濾,“排除不符合某個條件的元素”,也就是返回true的時候保留,返回false排除
????/**
?????*?通過Stream?filter篩選出分數(shù)大于60分的學生集合
?????*?
?????*?@param?students?待過濾的學生集合
?????*?@return?分數(shù)大于60分的學生集合
?????*/
????public?List<Student>?fetchPassedStudentsByStreamFilter(List<Student>?students)?{
????????return?students.stream().filter(student?->?student.getScore().compareTo(60)?>=?0).collect(Collectors.toList());
????}
sorted
排序,
????/**
?????*?學生按分數(shù)升序排列
?????*?
?????*?@param?students?學生集合
?????*?@return?排序后的學生集合
?????*/
????private?List<Student>?sortedByStreamSorted(List<Student>?students)?{
????????return?students.stream().sorted(Comparator.comparing(Student::getScore)).collect(Collectors.toList());
????}
如果要降序(大-->?。瑑H需再調(diào)用reversed方法Comparator.comparing(Student::getScore).reversed())
這就是聲明式編程,你只管叫它做什么,而不像命令式編程叫它如何做。
reduce
對于reduce操作,不建議在現(xiàn)實中使用。
如果你有累加、求最大值、最小值的需求,Stream封裝了更簡單的方法。
????/**
?????*?T?reduce(T?identity,?BinaryOperator<T>?accumulator);
?????*?賦初始值為1,對集合中的元素進行累加
?????*?
?????*?@param?numbers?集合元素
?????*?@return?累加結果
?????*/
????private?Integer?calcTotal2(List<Integer>?numbers)?{
????????return?numbers.stream().reduce(1,?(total,?number)?->?total?+?number);
????}
min || max
顧名思義,求取集合中的最小值和最大值。
Java8對Comparator
接口提供了新的靜態(tài)方法comparing
,這個方法返回Comparator
對象,以前我們需要手動實現(xiàn)compare
比較,現(xiàn)在我們只需要調(diào)用Comparator.comparing
靜態(tài)方法即可。
????/**
?????*?通過Stream?min計算集合中的最小值
?????*?
?????*?@param?numbers?集合
?????*?@return?最小值
?????*/
????private?Integer?minByStreamMin(List<Integer>?numbers)?{
????????return?numbers.stream().min(Comparator.comparingInt(Integer::intValue)).get();
????}
????/**
?????*?通過Stream?min計算學生集合中的最低成績
?????*?
?????*?@param?students?學生集合
?????*?@return?最低成績
?????*/
????private?Double?minScoreByStreamMin(List<Student>?students)?{
????????Student?minScoreStudent?=?students.stream().min(Comparator.comparing(Student::getScore)).get();
????????return?minScoreStudent.getScore();
????}
summaryStatistics
求和操作也是常用的操作,利用reduce會讓代碼晦澀難懂,特別是復雜的對象類型。
????/**
?????*?學生類型的集合常用計算
?????*?
?????*?@param?students?學生
?????*/
????private?void?calc(List<Student>?students)?{
????????DoubleSummaryStatistics?summaryStatistics?=
????????????students.stream().mapToDouble(Student::getScore).summaryStatistics();
????????System.out.println("平均分:"?+?summaryStatistics.getAverage());
????????System.out.println("總分:"?+?summaryStatistics.getSum());
????????System.out.println("最高分:"?+?summaryStatistics.getMax());
????????System.out.println("最低分:"?+?summaryStatistics.getMin());
????}
Collectors
前面的大部分操作都是以collect(Collectors.toList())
結尾,看多了自然也大概猜得到它是將流轉(zhuǎn)換為集合對象。最大的功勞當屬Java8新提供的類——Collectors
收集器。
示例給出比較常見的List和Map的轉(zhuǎn)換,
????/**
?????*?將學生類型的集合轉(zhuǎn)換為只包含名字的集合
?????*?
?????*?@param?students?學生集合
?????*?@return?學生姓名集合
?????*/
????private?List<String>?translateNames(List<Student>?students)?{
????????return?students.stream().map(Student::getStudentName).collect(Collectors.toList());
????}
????/**
?????*?將學生類型的集合轉(zhuǎn)換為Map類型,key=學號,value=學生
?????*?
?????*?@param?students?學生集合
?????*?@return?學生Map
?????*/
????private?Map<Long,?Student>?translateStudentMap(List<Student>?students)?{
????????return?students.stream().collect(Collectors.toMap(Student::getStudentNumber,?student?->?student));
????}
小結
以上內(nèi)容沒有多高深,主要為了讓大家知道不同情景下該使用哪種API,如果有同學都不熟悉這些,還怎么寫出優(yōu)雅的代碼呢,面試通過也成問題,像我就喜歡編程功底扎實的(手動狗頭)。