在想很好了解 Stream 以前,頗有必要簡單的瞭解下函數式變成以及Lambda的概念,能夠閱讀另一篇html
Java8新特性之Lambdajava
你們回憶下平常學習工做中使用的最多的 Java API 是什麼?相信不少人的答案和我同樣都是集合。咱們選擇適合的集合數據結構存儲數據,而咱們之於集合最多的操做就是遍歷,實現查詢,統計,過濾,合併等業務。程序員
外部迭代:經過 for循環,Iterator迭代器遍歷集合,手動的拿到集合中每一個元素進行相應處理算法
內部迭代:只提供對集合中元素的處理邏輯,遍歷過程交給庫類,Java5提供了foreach,Java8提供了Streamapi
本文要介紹的Stream屬於內部迭代,以前咱們已經有了foreach減小了咱們的代碼量,爲何咱們還須要Stream呢?數組
上文咱們大概知道了Stream主要服務與集合,流的過程能夠總結爲三步:數據結構
第一步流加載集合數據,庫類完成咱們不須要關心,要想使用好Stream有必要從不一樣維度瞭解主要的操做併發
下圖給出了Stream給出的一些經常使用api的基本信息dom
爲了不自動裝箱拆箱消耗性能,Stream爲咱們提供了IntStream、DoubleStream和LongStream,分別將流中的元素特化爲int、long和double,這些特殊的流中提供了range,sum,max等數字類型經常使用的apiide
當咱們面對計算是否密集的應用開發時,爲了充分利用硬件資源,能夠簡單的經過改變parallel方法將流變成並行執行,可是在使用時有以下注意事項。
相信你們最關心的仍是實際開發中能幫助咱們解決哪些問題,經過一些簡單案例熟悉各類操做的用法
Stream<Object> empty = Stream.empty();
Stream<String> stringStream = Stream.of("1", "2", "3");
String[] strings = { "1", "2", "3"}; Stream<String> stream = Arrays.stream(strings);
List<String> strings1 = Arrays.asList("1", "2", "3"); Stream<String> stream1 = strings1.stream();
Stream<String> lines = Files.lines(Paths.get("/c/mnt/"));
// 無限流,流從0開始,下面的每一個元素依次加2 Stream<Integer> iterate = Stream.iterate(0, num -> num + 2); // 無限流,流中每一個元素都是 0~1 隨機數 Stream<Double> generate = Stream.generate(Math::random);
// 生成0到10的int流 IntStream intStream = IntStream.rangeClosed(0, 10); // 生成0到9的int流 IntStream intStream1 = IntStream.range(0, 10);
// 生成有字符串a和數字1的異構的流 Stream.builder().add("a").add(1).build().forEach(System.out::print);
Stream.concat( Stream.of("1", 22, "333"), Stream.of("1", 22, 333) ).forEach(System.out::print);
在介紹中間操做時,爲了方便學習演示使用到了終端操做中的foreach方法,做用就和咱們寫的foreach循環相似,遍歷執行,不返回值。
// 過濾全部空字符串,注意是返回 true的留下 Stream.of("1", null, "2", "", "3") .filter(StringUtils::isNotEmpty) .forEach(System.out::print);
// 去掉重複的2 Stream.of("1", "2", "2", "2", "3") .distinct() .forEach(System.out::print);
// 跳過前兩個元素 Stream.of("1", "2", "3", "4", "5") .skip(2) .forEach(System.out::print);
// 與skip相反,只留下前2個 Stream.of("1", "2", "3", "4", "5") .limit(2) .forEach(System.out::print);
@Data @AllArgsConstructor public class Person { private String name; } // 將流中每一個字符串轉爲Person實例 Stream.of("1", "2", "3") .map(Person::new) .forEach(System.out::print); // 將流每一個字符串變成其長度,爲了不自動拆箱,使用mapToInt轉爲IntStream Stream.of("1", "2", "3") .mapToInt(String::length) .forEach(System.out::print);
用法和map相似,當咱們須要把流中的每一個元素全映射另一個流,也就是數據在流中流裏面,這時候操做就不方便,藉助flatMap,咱們能夠把全部第二層流中的元素合併到最外層流
// 每一個字符串按照逗號分隔合併成一個流 Stream.of("1,2,3", "4,5,6", "7,8,9") .flatMap(a -> Stream.of(a.split(","))) .forEach(System.out::print);
Stream.of("4", "3", "5") .sorted() .forEach(System.out::print); Stream.of("4", "3", "5") .sorted(Comparator.naturalOrder()) .forEach(System.out::print);
// 串行流轉並行流 Stream.of("1", "2", "3").parallel(); // 並行流轉串行流 Arrays.asList("1", "2", "3").parallelStream().sequential(); // 在並行流中加上unordered,使得流變成無序,提供並行效率,此時limit至關於隨機取2個元素 Arrays.asList("1", "2", "3", "4", "5") .parallelStream() .unordered() .limit(2).forEach(System.out::print);
用於debug調試代碼,在每次執行操做前,看一眼元素
// 每一個元素會打印兩遍 Stream.of("1", "2", "3").peek(System.out::print).forEach(System.out::print);
Stream.of("1", "2", "3").forEach(System.out::print); // 並行流中使用用於保持有序 Arrays.asList("1", "2", "3").parallelStream().forEachOrdered(System.out::print);
// 總個數,返回true|false Stream.of("1", "2", "3").count(); // 最大元素,返回Optional Stream.of("1", "2", "3").max(Comparator.naturalOrder()); // 最小元素,返回Optional Stream.of("1", "2", "3").min(Comparator.naturalOrder());
以 IntStream 舉例
// 累加求和 Stream.of("1", "2", "3").mapToInt(Integer::valueOf).sum(); // 求平均數 Stream.of("1", "2", "3").mapToInt(Integer::valueOf).average(); // 總和,最大值,最小值,平均數,總個數,應有盡有 Stream.of("1", "2", "3").mapToInt(Integer::valueOf).summaryStatistics();
返回true|false
// 是否有長度大於 2 的字符串 Stream.of("1", "22", "333").anyMatch(s -> s.length() > 2); // 是否一個長度大於 2 的字符串也沒有 Stream.of("1", "22", "333").noneMatch(s -> s.length() > 2); // 是否字符串長度全大於 2 Stream.of("1", "22", "333").allMatch(s -> s.length() > 2);
因爲是短路操做,因此只有在串行流中findAny和findFirst才區別明顯
// 找到流中任意一個元素,普通流通常也返回第一個元素,並行流中返回任意元素 Stream.of("1", "22", "333").findAny(); // 找到流中第一個元素,普通流和並行流都同樣 Stream.of("1", "22", "333").findFirst();
把全部處理結果彙總,Collectors收集器裏提供了不少經常使用的彙總操做
// 將結果彙總成一個list Stream.of("1", "22", "333").collect(Collectors.toList());
谷歌著名的map-reduce理想,用於最後彙總結果
// 0做爲起始的pre,流的結果等於pre乘以自身再加一,直到curr到達最後一個元素 // (1) pre = 0 curr = 1 計算 pre = 0 * 1 + 1 = 1 // (2) pre = 1 curr = 2 計算 pre = 1 * 2 + 1 = 3 // (3) pre = 3 curr = 3 計算 pre = 3 * 3 + 1 = 10 // (4) pre = 10 curr = null 返回結果 10 IntStream.of(1, 2, 3).reduce(0, (pre, curr) -> pre * curr + 1); // 固然若是不設置初始值,流中第一個元素就是pre IntStream.of(1, 2, 3).reduce((pre, curr) -> pre * curr + 1);
Stream.of("1", "22", "333").toArray();
Stream.of("1", "2", "3").iterator();
Stream.of("1", "2", "3").spliterator();
Stream.of("1", "2", "3").isParallel();
在介紹 collect 操做中,咱們用了 Collectors 中提供的 toList 方法將結果彙總成List,collect 是很經常使用的操做Collectors中有不少有用的方法值得熟悉一下,其實不少終端方法都是 collect 的快捷寫法,若是都不能知足需求咱們還能夠本身實現一個
// 轉list List<String> collect = Stream.of("1", "2", "3").collect(Collectors.toList()); // 轉set Set<String> collect = Stream.of("1", "2", "3").collect(Collectors.toSet()); // 轉map,key爲字符串長度,value爲字符串自己 Map<Integer, String> collect = Stream.of("1", "2", "3") .collect(Collectors.toMap(String::length, Function.identity())); // 轉併發版map,key爲字符串長度,value爲字符串自己 Map<Integer, String> collect = Stream.of("1", "2", "3") .collect(Collectors.toConcurrentMap(String::length, Function.identity())); // 轉指定類型集合 ArrayList<String> collect = Stream.of("1", "2", "3") .collect(Collectors.toCollection(ArrayList::new));
// 拼接成一個字符串 String collect = Stream.of("1", "2", "3").collect(Collectors.joining()); // 拼接成一個字符串,逗號分隔 String collect = Stream.of("1", "2", "3").collect(Collectors.joining(","));
都有對應簡化版,一些更加靈活多變的操做能夠用Collectors
// 和終端操做中的 max 等價 Stream.of("1", "2", "3").collect(Collectors.maxBy(Comparator.naturalOrder())); // 和終端操做中的 min 等價 Stream.of("1", "2", "3").collect(Collectors.minBy(Comparator.naturalOrder())); // 和終端操做中的 count 等價 Stream.of("1", "2", "3").collect(Collectors.counting(Integer::valueOf)); // 和數值流終端操做中的 sum 等價 Stream.of("1", "2", "3").collect(Collectors.summingInt(Integer::valueOf)); // 和數值流終端操做中的 average 等價 Stream.of("1", "2", "3").collect(Collectors.averagingInt(Integer::valueOf)); // 和數值流終端操做中的 summaryStatistics 等價 Stream.of("1", "2", "3").collect(Collectors.summarizingInt(Integer::valueOf));
能夠和其餘收集器方法任意組合
// 以字符串長度分紅3組,map的key爲長度,value爲對應長度字符串list Map<Integer, List<String>> collect = Stream.of("1", "22", "33", "4", "555") .collect(Collectors.groupingBy(String::length)); // 以字符串長度分紅3組,map的key爲長度,value爲對應的元素個數 Map<Integer, Long> collect = Stream.of("1", "22", "33", "4", "555") .collect(Collectors.groupingBy(String::length, Collectors.counting())); // 這個例子沒有實際意義,展現咱們能夠進行二級分組,長度分組完畢,再組內以hash值分組 Map<Integer, Map<Integer, List<String>>> collect = Stream.of("1", "22", "33", "4", "555") .collect(Collectors.groupingBy( String::length, Collectors.groupingBy(String::hashCode) ));
分區時特殊的分組,使用方法相似,特殊是經過謂詞表達式只能分紅兩組,true是一組,false是一組
// 以長度大於2爲標準分區 Map<Boolean, List<String>> collect = Stream.of("1", "22", "33", "4", "555") .collect(Collectors.partitioningBy(s -> s.length() > 2));
// 和終端操做 reduce 等價 Stream.of("1", "22", "33", "4", "555") .collect(Collectors.reducing(0, Integer::valueOf, Integer::sum))
// 把流彙總成list,而後再求出其容量 Integer collect = Stream.of("1", "22", "33", "4", "555") .collect(Collectors.collectingAndThen(Collectors.toList(), List::size));
有些時候默認的實現有缺陷,或者追求更高的性能咱們須要本身實現收集器。只要實現 Collector<T, A , R>接口 中的方法咱們就能夠得到本身的收集器,其中 T 是元素泛型,A是累加器結果,R是最終返回結果,全部首先咱們來看下要實現哪些方法
public static final Collector<String, List<String>, List<String>> myToList = Collector.of( // supplier: 建立 A(ArrayList) ArrayList::new, // accumulator:把每一個元素放入 A 中 (list, el) -> list.add(el), // combiner:若是並行拆分紅多個流,直接 addAll 合併 // 若是不想支持並行能夠寫個空,或拋UnsupportedOperationException異常 (listA, listB) -> { listA.addAll(listB); return listA; }, // finisher:不作任何事情,直接返回 A Function.identity(), // characteristics...:表示 A R 類型相同, 且支持並行流 Collector.Characteristics.IDENTITY_FINISH, Collector.Characteristics.CONCURRENT ); // 自定義收集器轉成list List<String> collect = Stream.of("1", "22", "33", "4", "555").collect(myToList);