原文地址: 深藍至尊
一. 流式處理簡介
在我接觸到java8流式處理的時候,我的第一感覺是流式處理讓集合操作變得簡潔了許多召庞,通常我們需要多行代碼才能完成的操作,借助于流式處理可以在一行中實現(xiàn)侧蘸。比如我們希望對一個包含整數(shù)的集合中篩選出所有的偶數(shù)裁眯,并將其封裝成為一個新的List返回,那么在java8之前讳癌,我們需要通過如下代碼實現(xiàn):
<pre style="margin: 0px; padding: 0px; white-space: pre-wrap; overflow-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important;">List<Integer> evens = new ArrayList<>(); for (final Integer num : nums) { if (num % 2 == 0) {
evens.add(num);
}
}</pre>
通過java8的流式處理穿稳,我們可以將代碼簡化為:
<pre style="margin: 0px; padding: 0px; white-space: pre-wrap; overflow-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important;">List<Integer> evens = nums.stream().filter(num -> num % 2 == 0).collect(Collectors.toList());</pre>
先簡單解釋一下上面這行語句的含義,stream()
操作將集合轉換成一個流晌坤,filter()
執(zhí)行我們自定義的篩選處理逢艘,這里是通過lambda表達式篩選出所有偶數(shù),最后我們通過collect()
對結果進行封裝處理骤菠,并通過Collectors.toList()
指定其封裝成為一個List集合返回它改。
由上面的例子可以看出,java8的流式處理極大的簡化了對于集合的操作商乎,實際上不光是集合央拖,包括數(shù)組、文件等,只要是可以轉換成流鲜戒,我們都可以借助流式處理专控,類似于我們寫SQL語句一樣對其進行操作。java8通過內(nèi)部迭代來實現(xiàn)對流的處理遏餐,一個流式處理可以分為三個部分:轉換成流伦腐、中間操作、終端操作失都。如下圖:
以集合為例柏蘑,一個流式處理的操作我們首先需要調用stream()
函數(shù)將其轉換成流,然后再調用相應的中間操作
達到我們需要對集合進行的操作粹庞,比如篩選咳焚、轉換等,最后通過終端操作
對前面的結果進行封裝信粮,返回我們需要的形式黔攒。
二. 中間操作
我們定義一個簡單的學生實體類,用于后面的例子演示:
[](javascript:void(0); "復制代碼")
<pre style="margin: 0px; padding: 0px; white-space: pre-wrap; overflow-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important;">public class Student { /** 學號 /
private long id; private String name; private int age; /* 年級 /
private int grade; /* 專業(yè) /
private String major; /* 學校 */
private String school; // 省略getter和setter
}</pre>
<pre style="margin: 0px; padding: 0px; white-space: pre-wrap; overflow-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important;">// 初始化
List<Student> students = new ArrayList<Student>() {
{
add(new Student(20160001, "孔明", 20, 1, "土木工程", "武漢大學"));
add(new Student(20160002, "伯約", 21, 2, "信息安全", "武漢大學"));
add(new Student(20160003, "玄德", 22, 3, "經(jīng)濟管理", "武漢大學"));
add(new Student(20160004, "云長", 21, 2, "信息安全", "武漢大學"));
add(new Student(20161001, "翼德", 21, 2, "機械與自動化", "華中科技大學"));
add(new Student(20161002, "元直", 23, 4, "土木工程", "華中科技大學"));
add(new Student(20161003, "奉孝", 23, 4, "計算機科學", "華中科技大學"));
add(new Student(20162001, "仲謀", 22, 3, "土木工程", "浙江大學"));
add(new Student(20162002, "魯肅", 23, 4, "計算機科學", "浙江大學"));
add(new Student(20163001, "丁奉", 24, 5, "土木工程", "南京大學"));
}
};</pre>
](javascript:void(0); "復制代碼")
2.1 過濾
過濾强缘,顧名思義就是按照給定的要求對集合進行篩選滿足條件的元素,java8提供的篩選操作包括:filter不傅、distinct旅掂、limit、skip访娶。
filter
在前面的例子中我們已經(jīng)演示了如何使用filter商虐,其定義為:Stream<T> filter(Predicate<? super T> predicate)
,filter接受一個謂詞Predicate
崖疤,我們可以通過這個謂詞定義篩選條件秘车,在介紹lambda表達式時我們介紹過Predicate
是一個函數(shù)式接口,其包含一個test(T t)
方法劫哼,該方法返回boolean
《E浚現(xiàn)在我們希望從集合students
中篩選出所有武漢大學的學生,那么我們可以通過filter來實現(xiàn)权烧,并將篩選操作作為參數(shù)傳遞給filter:
<pre style="margin: 0px; padding: 0px; white-space: pre-wrap; overflow-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important;">List<Student> whuStudents = students.stream()
.filter(student -> "武漢大學".equals(student.getSchool()))
.collect(Collectors.toList());</pre>
distinct
distinct操作類似于我們在寫SQL語句時眯亦,添加的DISTINCT
關鍵字,用于去重處理般码,distinct基于Object.equals(Object)
實現(xiàn)妻率,回到最開始的例子,假設我們希望篩選出所有不重復的偶數(shù)板祝,那么可以添加distinct操作:
<pre style="margin: 0px; padding: 0px; white-space: pre-wrap; overflow-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important;">List<Integer> evens = nums.stream()
.filter(num -> num % 2 == 0).distinct()
.collect(Collectors.toList());</pre>
limit
limit操作也類似于SQL語句中的LIMIT
關鍵字宫静,不過相對功能較弱,limit返回包含前n個元素的流,當集合大小小于n時孤里,則返回實際長度温技,比如下面的例子返回前兩個專業(yè)為土木工程
專業(yè)的學生:
<pre style="margin: 0px; padding: 0px; white-space: pre-wrap; overflow-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important;">List<Student> civilStudents = students.stream()
.filter(student -> "土木工程".equals(student.getMajor())).limit(2)
.collect(Collectors.toList());</pre>
說到limit,不得不提及一下另外一個流操作:sorted
扭粱。該操作用于對流中元素進行排序舵鳞,sorted要求待比較的元素必須實現(xiàn)Comparable
接口,如果沒有實現(xiàn)也不要緊琢蛤,我們可以將比較器作為參數(shù)傳遞給sorted(Comparator<? super T> comparator)
蜓堕,比如我們希望篩選出專業(yè)為土木工程的學生,并按年齡從小到大排序博其,篩選出年齡最小的兩個學生套才,那么可以實現(xiàn)為:
<pre style="margin: 0px; padding: 0px; white-space: pre-wrap; overflow-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important;">List<Student> sortedCivilStudents = students.stream()
.filter(student -> "土木工程".equals(student.getMajor())).sorted((s1, s2) -> s1.getAge() - s2.getAge())
.limit(2)
.collect(Collectors.toList());</pre>
skip
skip操作與limit操作相反,如同其字面意思一樣慕淡,是跳過前n個元素背伴,比如我們希望找出排序在2之后的土木工程專業(yè)的學生,那么可以實現(xiàn)為:
<pre style="margin: 0px; padding: 0px; white-space: pre-wrap; overflow-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important;">List<Student> civilStudents = students.stream()
.filter(student -> "土木工程".equals(student.getMajor()))
.skip(2)
.collect(Collectors.toList());</pre>
通過skip峰髓,就會跳過前面兩個元素傻寂,返回由后面所有元素構造的流,如果n大于滿足條件的集合的長度携兵,則會返回一個空的集合疾掰。
2.2 映射
在SQL中,借助SELECT
關鍵字后面添加需要的字段名稱徐紧,可以僅輸出我們需要的字段數(shù)據(jù)静檬,而流式處理的映射操作也是實現(xiàn)這一目的,在java8的流式處理中并级,主要包含兩類映射操作:map和flatMap拂檩。
map
舉例說明,假設我們希望篩選出所有專業(yè)為計算機科學的學生姓名嘲碧,那么我們可以在filter篩選的基礎之上稻励,通過map將學生實體映射成為學生姓名字符串,具體實現(xiàn)如下:
<pre style="margin: 0px; padding: 0px; white-space: pre-wrap; overflow-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important;">List<String> names = students.stream()
.filter(student -> "計算機科學".equals(student.getMajor()))
.map(Student::getName).collect(Collectors.toList());</pre>
除了上面這類基礎的map呀潭,java8還提供了mapToDouble(ToDoubleFunction<? super T> mapper)
钉迷,mapToInt(ToIntFunction<? super T> mapper)
,mapToLong(ToLongFunction<? super T> mapper)
钠署,這些映射分別返回對應類型的流糠聪,java8為這些流設定了一些特殊的操作,比如我們希望計算所有專業(yè)為計算機科學學生的年齡之和谐鼎,那么我們可以實現(xiàn)如下:
<pre style="margin: 0px; padding: 0px; white-space: pre-wrap; overflow-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important;">int totalAge = students.stream()
.filter(student -> "計算機科學".equals(student.getMajor()))
.mapToInt(Student::getAge).sum();</pre>
通過將Student按照年齡直接映射為IntStream
舰蟆,我們可以直接調用提供的sum()
方法來達到目的,此外使用這些數(shù)值流的好處還在于可以避免jvm裝箱操作所帶來的性能消耗。
flatMap
flatMap與map的區(qū)別在于 flatMap是將一個流中的每個值都轉成一個個流身害,然后再將這些流扁平化成為一個流 味悄。舉例說明,假設我們有一個字符串數(shù)組String[] strs = {"java8", "is", "easy", "to", "use"};
塌鸯,我們希望輸出構成這一數(shù)組的所有非重復字符侍瑟,那么我們可能首先會想到如下實現(xiàn):
<pre style="margin: 0px; padding: 0px; white-space: pre-wrap; overflow-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important;">List<String[]> distinctStrs = Arrays.stream(strs)
.map(str -> str.split("")) // 映射成為Stream<String[]>
.distinct()
.collect(Collectors.toList());</pre>
在執(zhí)行map操作以后,我們得到是一個包含多個字符串(構成一個字符串的字符數(shù)組)的流丙猬,此時執(zhí)行distinct操作是基于在這些字符串數(shù)組之間的對比涨颜,所以達不到我們希望的目的,此時的輸出為:
<pre style="margin: 0px; padding: 0px; white-space: pre-wrap; overflow-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important;">[j, a, v, a, 8]
[i, s]
[e, a, s, y]
[t, o]
[u, s, e]</pre>
distinct只有對于一個包含多個字符的流進行操作才能達到我們的目的茧球,即對Stream<String>
進行操作庭瑰。此時flatMap就可以達到我們的目的:
<pre style="margin: 0px; padding: 0px; white-space: pre-wrap; overflow-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important;">List<String> distinctStrs = Arrays.stream(strs)
.map(str -> str.split("")) // 映射成為Stream<String[]>
.flatMap(Arrays::stream) // 扁平化為Stream<String>
.distinct()
.collect(Collectors.toList());</pre>
flatMap將由map映射得到的Stream<String[]>
,轉換成由各個字符串數(shù)組映射成的流Stream<String>
抢埋,再將這些小的流扁平化成為一個由所有字符串構成的大流Steam<String>
弹灭,從而能夠達到我們的目的。
與map類似揪垄,flatMap也提供了針對特定類型的映射操作:flatMapToDouble(Function<? super T,? extends DoubleStream> mapper)
穷吮,flatMapToInt(Function<? super T,? extends IntStream> mapper)
,flatMapToLong(Function<? super T,? extends LongStream> mapper)
福侈。
三. 終端操作
終端操作是流式處理的最后一步酒来,我們可以在終端操作中實現(xiàn)對流查找、歸約等操作肪凛。
3.1 查找
allMatch
allMatch用于檢測是否全部都滿足指定的參數(shù)行為,如果全部滿足則返回true辽社,例如我們希望檢測是否所有的學生都已滿18周歲伟墙,那么可以實現(xiàn)為:
<pre style="margin: 0px; padding: 0px; white-space: pre-wrap; overflow-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important;">boolean isAdult = students.stream().allMatch(student -> student.getAge() >= 18);</pre>
anyMatch
anyMatch則是檢測是否存在一個或多個滿足指定的參數(shù)行為,如果滿足則返回true滴铅,例如我們希望檢測是否有來自武漢大學的學生戳葵,那么可以實現(xiàn)為:
<pre style="margin: 0px; padding: 0px; white-space: pre-wrap; overflow-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important;">boolean hasWhu = students.stream().anyMatch(student -> "武漢大學".equals(student.getSchool()));</pre>
noneMathch
noneMatch用于檢測是否不存在滿足指定行為的元素,如果不存在則返回true汉匙,例如我們希望檢測是否不存在專業(yè)為計算機科學的學生拱烁,可以實現(xiàn)如下:
<pre style="margin: 0px; padding: 0px; white-space: pre-wrap; overflow-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important;">boolean noneCs = students.stream().noneMatch(student -> "計算機科學".equals(student.getMajor()));</pre>
findFirst
findFirst用于返回滿足條件的第一個元素,比如我們希望選出專業(yè)為土木工程的排在第一個學生噩翠,那么可以實現(xiàn)如下:
<pre style="margin: 0px; padding: 0px; white-space: pre-wrap; overflow-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important;">Optional<Student> optStu = students.stream().filter(student -> "土木工程".equals(student.getMajor())).findFirst();</pre>
findFirst不攜帶參數(shù)戏自,具體的查找條件可以通過filter設置,此外我們可以發(fā)現(xiàn)findFirst返回的是一個Optional類型伤锚,關于該類型的具體講解可以參考上一篇:Java8新特性 – Optional類擅笔。
findAny
findAny相對于findFirst的區(qū)別在于,findAny不一定返回第一個,而是返回任意一個猛们,比如我們希望返回任意一個專業(yè)為土木工程的學生念脯,可以實現(xiàn)如下:
<pre style="margin: 0px; padding: 0px; white-space: pre-wrap; overflow-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important;">Optional<Student> optStu = students.stream().filter(student -> "土木工程".equals(student.getMajor())).findAny();</pre>
實際上對于順序流式處理而言,findFirst和findAny返回的結果是一樣的弯淘,至于為什么會這樣設計绿店,是因為在下一篇我們介紹的并行流式處理,當我們啟用并行流式處理的時候庐橙,查找第一個元素往往會有很多限制假勿,如果不是特別需求,在并行流式處理中使用findAny的性能要比findFirst好怕午。
3.2 歸約
前面的例子中我們大部分都是通過collect(Collectors.toList())
對數(shù)據(jù)封裝返回废登,如我的目標不是返回一個新的集合,而是希望對經(jīng)過參數(shù)化操作后的集合進行進一步的運算郁惜,那么我們可用對集合實施歸約操作堡距。java8的流式處理提供了reduce
方法來達到這一目的。
前面我們通過mapToInt將Stream<Student>
映射成為IntStream
兆蕉,并通過IntStream
的sum方法求得所有學生的年齡之和羽戒,實際上我們通過歸約操作,也可以達到這一目的虎韵,實現(xiàn)如下:
](javascript:void(0); "復制代碼")
<pre style="margin: 0px; padding: 0px; white-space: pre-wrap; overflow-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important;">// 前面例子中的方法
int totalAge = students.stream()
.filter(student -> "計算機科學".equals(student.getMajor()))
.mapToInt(Student::getAge).sum(); // 歸約操作
int totalAge = students.stream()
.filter(student -> "計算機科學".equals(student.getMajor()))
.map(Student::getAge)
.reduce(0, (a, b) -> a + b); // 進一步簡化
int totalAge2 = students.stream()
.filter(student -> "計算機科學".equals(student.getMajor()))
.map(Student::getAge)
.reduce(0, Integer::sum); // 采用無初始值的重載版本易稠,需要注意返回Optional
Optional<Integer> totalAge = students.stream()
.filter(student -> "計算機科學".equals(student.getMajor()))
.map(Student::getAge)
.reduce(Integer::sum); // 去掉初始值</pre>
](javascript:void(0); "復制代碼")
3.3 收集
前面利用collect(Collectors.toList())
是一個簡單的收集操作,是對處理結果的封裝包蓝,對應的還有toSet
驶社、toMap
,以滿足我們對于結果組織的需求测萎。這些方法均來自于java.util.stream.Collectors
亡电,我們可以稱之為收集器。
3.3.1 歸約
收集器也提供了相應的歸約操作硅瞧,但是與reduce在內(nèi)部實現(xiàn)上是有區(qū)別的份乒,收集器更加適用于可變?nèi)萜魃系臍w約操作,這些收集器廣義上均基于Collectors.reducing()
實現(xiàn)腕唧。
例1:求學生的總人數(shù)
<pre style="margin: 0px; padding: 0px; white-space: pre-wrap; overflow-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important;">long count = students.stream().collect(Collectors.counting()); // 進一步簡化
long count = students.stream().count();</pre>
例2:求年齡的最大值和最小值
[](javascript:void(0); "復制代碼")
<pre style="margin: 0px; padding: 0px; white-space: pre-wrap; overflow-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important;">// 求最大年齡
Optional<Student> olderStudent = students.stream().collect(Collectors.maxBy((s1, s2) -> s1.getAge() - s2.getAge())); // 進一步簡化
Optional<Student> olderStudent2 = students.stream().collect(Collectors.maxBy(Comparator.comparing(Student::getAge))); // 求最小年齡
Optional<Student> olderStudent3 = students.stream().collect(Collectors.minBy(Comparator.comparing(Student::getAge)));</pre>
](javascript:void(0); "復制代碼")
例3:求年齡總和
<pre style="margin: 0px; padding: 0px; white-space: pre-wrap; overflow-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important;">int totalAge4 = students.stream().collect(Collectors.summingInt(Student::getAge));</pre>
對應的還有summingLong
或辖、summingDouble
。
例4:求年齡的平均值
<pre style="margin: 0px; padding: 0px; white-space: pre-wrap; overflow-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important;">double avgAge = students.stream().collect(Collectors.averagingInt(Student::getAge));</pre>
對應的還有averagingLong
枣接、averagingDouble
颂暇。
例5:一次性得到元素個數(shù)、總和月腋、均值蟀架、最大值瓣赂、最小值
<pre style="margin: 0px; padding: 0px; white-space: pre-wrap; overflow-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important;">IntSummaryStatistics statistics = students.stream().collect(Collectors.summarizingInt(Student::getAge));</pre>
輸出:
<pre style="margin: 0px; padding: 0px; white-space: pre-wrap; overflow-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important;">IntSummaryStatistics{count=10, sum=220, min=20, average=22.000000, max=24}</pre>
對應的還有summarizingLong
、summarizingDouble
片拍。
例6:字符串拼接
<pre style="margin: 0px; padding: 0px; white-space: pre-wrap; overflow-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important;">String names = students.stream().map(Student::getName).collect(Collectors.joining()); // 輸出:孔明伯約玄德云長翼德元直奉孝仲謀魯肅丁奉
String names = students.stream().map(Student::getName).collect(Collectors.joining(", ")); // 輸出:孔明, 伯約, 玄德, 云長, 翼德, 元直, 奉孝, 仲謀, 魯肅, 丁奉</pre>
3.3.2 分組
在數(shù)據(jù)庫操作中煌集,我們可以通過GROUP BY
關鍵字對查詢到的數(shù)據(jù)進行分組,java8的流式處理也為我們提供了這樣的功能Collectors.groupingBy
來操作集合捌省。比如我們可以按學校對上面的學生進行分組:
<pre style="margin: 0px; padding: 0px; white-space: pre-wrap; overflow-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important;">Map<String, List<Student>> groups = students.stream().collect(Collectors.groupingBy(Student::getSchool));</pre>
groupingBy
接收一個分類器Function<? super T, ? extends K> classifier
苫纤,我們可以自定義分類器來實現(xiàn)需要的分類效果。
上面演示的是一級分組纲缓,我們還可以定義多個分類器實現(xiàn) 多級分組卷拘,比如我們希望在按學校分組的基礎之上再按照專業(yè)進行分組,實現(xiàn)如下:
<pre style="margin: 0px; padding: 0px; white-space: pre-wrap; overflow-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important;">Map<String, Map<String, List<Student>>> groups2 = students.stream().collect(
Collectors.groupingBy(Student::getSchool, // 一級分組祝高,按學校
Collectors.groupingBy(Student::getMajor))); // 二級分組栗弟,按專業(yè)</pre>
實際上在groupingBy
的第二個參數(shù)不是只能傳遞groupingBy,還可以傳遞任意Collector
類型工闺,比如我們可以傳遞一個Collector.counting
乍赫,用以統(tǒng)計每個組的個數(shù):
<pre style="margin: 0px; padding: 0px; white-space: pre-wrap; overflow-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important;">Map<String, Long> groups = students.stream().collect(Collectors.groupingBy(Student::getSchool, Collectors.counting()));</pre>
如果我們不添加第二個參數(shù),則編譯器會默認幫我們添加一個Collectors.toList()
陆蟆。
3.3.3 分區(qū)
分區(qū)可以看做是分組的一種特殊情況雷厂,在分區(qū)中key只有兩種情況:true或false,目的是將待分區(qū)集合按照條件一分為二叠殷,java8的流式處理利用ollectors.partitioningBy()
方法實現(xiàn)分區(qū)改鲫,該方法接收一個謂詞,例如我們希望將學生分為武大學生和非武大學生林束,那么可以實現(xiàn)如下:
<pre style="margin: 0px; padding: 0px; white-space: pre-wrap; overflow-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important;">Map<Boolean, List<Student>> partition = students.stream().collect(Collectors.partitioningBy(student -> "武漢大學".equals(student.getSchool())));</pre>
分區(qū)相對分組的優(yōu)勢在于像棘,我們可以同時得到兩類結果,在一些應用場景下可以一步得到我們需要的所有結果壶冒,比如將數(shù)組分為奇數(shù)和偶數(shù)讲弄。
以上介紹的所有收集器均實現(xiàn)自接口java.util.stream.Collector
,該接口的定義如下:
](javascript:void(0); "復制代碼")
<pre style="margin: 0px; padding: 0px; white-space: pre-wrap; overflow-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important;">public interface Collector<T, A, R> { /** * A function that creates and returns a new mutable result container.
*
* @return a function which returns a new, mutable result container / Supplier<A> supplier(); /* * A function that folds a value into a mutable result container.
*
* @return a function which folds a value into a mutable result container / BiConsumer<A, T> accumulator(); /* * A function that accepts two partial results and merges them. The
* combiner function may fold state from one argument into the other and
* return that, or may return a new result container.
*
* @return a function which combines two partial results into a combined
* result / BinaryOperator<A> combiner(); /* * Perform the final transformation from the intermediate accumulation type
* {@code A} to the final result type {@code R}.
*
* <p>If the characteristic {@code IDENTITY_TRANSFORM} is
* set, this function may be presumed to be an identity transform with an
* unchecked cast from {@code A} to {@code R}.
*
* @return a function which transforms the intermediate result to the final
* result / Function<A, R> finisher(); /* * Returns a {@code Set} of {@code Collector.Characteristics} indicating
* the characteristics of this Collector. This set should be immutable.
*
* @return an immutable set of collector characteristics */ Set<Characteristics> characteristics();
}</pre>
[](javascript:void(0); "復制代碼")
我們也可以實現(xiàn)該接口來定義自己的收集器依痊,此處不再展開。
四. 并行流式數(shù)據(jù)處理
流式處理中的很多都適合采用 分而治之 的思想怎披,從而在處理集合較大時胸嘁,極大的提高代碼的性能,java8的設計者也看到了這一點凉逛,所以提供了 并行流式處理性宏。上面的例子中我們都是調用stream()
方法來啟動流式處理,java8還提供了parallelStream()
來啟動并行流式處理状飞,parallelStream()
本質上基于java7的Fork-Join框架實現(xiàn)毫胜,其默認的線程數(shù)為宿主機的內(nèi)核數(shù)书斜。
啟動并行流式處理雖然簡單,只需要將stream()
替換成parallelStream()
即可酵使,但既然是并行荐吉,就會涉及到多線程安全問題,所以在啟用之前要先確認并行是否值得(并行的效率不一定高于順序執(zhí)行)口渔,另外就是要保證線程安全样屠。此兩項無法保證,那么并行毫無意義缺脉,畢竟結果比速度更加重要痪欲,以后有時間再來詳細分析一下并行流式數(shù)據(jù)處理的具體實現(xiàn)和最佳實踐。