【前言】 java8新特性html
Java8 中的 Stream 是對集合(Collection)對象功能的加強,它專一於對集合對象進行各類很是便利、高效的聚合操做(aggregate operation),或者大批量數據操做 (bulk data operation)。它提供串行和並行兩種模式進行匯聚操做,併發模式可以充分利用多核處理器的優點,使用 fork/join 並行方式來拆分任務和加速處理過程。Stream API 藉助於一樣新出現的 Lambda 表達式,極大的提升編程效率和程序可讀性。
而在Java 的集合 API 中,僅僅有極少許的輔助型方法,更多的時候是程序員須要用 Iterator 來遍歷集合,完成相關的聚合應用邏輯。
所謂聚合操做就相似SQL語句同樣的操做, 好比filter, map, reduce, find, match, sorted等。java
Stream 不是集合元素,它不是數據結構並不保存數據,它是有關算法和計算的,它更像一個高級版本的 Iterator,用戶只要給出須要對其包含的元素執行什麼操做,Stream 會隱式地在內部進行遍歷,作出相應的數據轉換,好比 「過濾掉長度大於 10 的字符串」。而原始版本的 Iterator,用戶只能顯式地一個一個遍歷元素並對其執行某些操做。
Stream 如同一個迭代器(Iterator),單向,不可往復,數據只能遍歷一次,遍歷過一次後即用盡了。和迭代器不一樣的是,Stream 能夠並行化操做,迭代器只能命令式地、串行化操做,Stream 的並行操做依賴於 Java7 中的 Fork/Join 框架(JSR166y)來拆分任務和加速處理過程。git
中間操做(intermediate )
一個流能夠後面跟隨零個或多個 intermediate 操做。其目的是打開流,作出某種程度的數據映射/過濾,而後返回一個新的流,交給下一個操做使用。這類操做都是惰性化的(lazy),僅僅調用到這類方法,並無真正開始流的遍歷。
對應方法有map (mapToInt, flatMap 等)、 filter、 distinct、 sorted、 peek、 skip、 parallel、 sequential、 unordered等
終止操做(terminal )
一個流只能有一個 terminal 操做,當這個操做執行後,流就被使用盡了,沒法再被操做,這是流的最後一個操做。
Terminal 操做的執行,纔會真正開始流的遍歷,而且會生成一個結果或者一個錯誤。
對應方法有forEach、 forEachOrdered、 toArray、 reduce、 collect、 min、 max、 count、iterator
短路操做(short-circuiting)
對於一個 intermediate 操做,若是它接受的是一個無限大(infinite/unbounded)的 Stream,但返回一個有限的新 Stream。
對於一個 terminal 操做,若是它接受的是一個無限大的 Stream,但能在有限的時間計算出結果。
對應方法有anyMatch、 allMatch、 noneMatch、 findFirst、 findAny、 limit程序員
流的來源能夠是集合,數組,I/O channel, 產生器generator 等。
基本數值型,目前有三種對應的包裝類型 Stream:IntStream、LongStream、DoubleStream。github
示例以下:算法
/*************流的來源*************/ // 一、of方法 // of(T... values):返回含有多個T元素的Stream // of(T t):返回含有一個T元素的Stream Stream<String> single = Stream.of("a"); Stream<String> multiple = Stream.of("a", "b", "c"); // 二、generator方法,返回一個無限長度的Stream,其元素由Supplier接口的提供。 Stream<String> generate = Stream.generate(() -> "a"); Stream<Double> generateA = Stream.generate(new Supplier<Double>() { @Override public Double get() { return java.lang.Math.random(); } }); // lambda寫法 Stream<Double> generateB = Stream.generate(()-> java.lang.Math.random()); Stream<Double> generateC = Stream.generate(java.lang.Math::random); // 三、iterate方法,返回的也是一個無限長度的Stream,與generate方法不一樣的是,其是經過函數f迭代對給指定的元素種子而產生無限連續有序Stream,其中包含的元素能夠認爲是:seed,f(seed),f(f(seed))無限循環 Stream<Integer> iterate = Stream.iterate(1, item -> item + 2); // 無限流處理 iterate.limit(10).forEach(System.out::println); // 四、empty方法返回一個空的順序Stream,該Stream裏面不包含元素項。 Stream<Object> empty = Stream.empty(); // 五、Collection接口和數組的默認方法 String chars[] = new String[]{"a", "b", "c"}; Arrays.stream(chars).forEach(System.out::println); List list = Arrays.asList(chars); list.stream().forEach(System.out::println);
當你熟悉使用Lambda表達式時,就能夠很方便的進行流相關的操做了,提升代碼的編寫效率和程序的可讀性。編程
/*************流的操做*************/ //concat 將兩個Stream鏈接在一塊兒,合成一個Stream。若兩個輸入的Stream都時排序的,則新Stream也是排序的;若輸入的Stream中任何一個是並行的,則新的Stream也是並行的;若關閉新的Stream時,原兩個輸入的Stream都將執行關閉處理。 Stream.concat(Stream.of(1, 2, 3), Stream.of(4, 5)) .forEach(integer -> System.out.print(integer + " ")); //distinct 去除掉原Stream中重複的元素,生成的新Stream中沒有沒有重複的元素。 Stream.of(1,1,3,4,3).distinct().forEach(System.out::println); //filter 對原Stream按照指定條件過濾,過濾出知足條件的元素。 Stream.of(1,1,3,4,3).filter(x -> x > 2).forEach(System.out::println); //map方法將對於Stream中包含的元素使用給定的轉換函數進行轉換操做,新生成的Stream只包含轉換生成的元素。 //爲了提升處理效率,官方已封裝好了,三種變形:mapToDouble,mapToInt,mapToLong,將原Stream中的數據類型,轉換爲double,int或者long類型。 Stream.of("a", "b", "c") .map(item -> item.toUpperCase()) // .map(String::toUpperCase) .forEach(System.out::println); // flatMap方法與map方法相似,都是將原Stream中的每個元素經過轉換函數轉換, // 不一樣的是,該換轉函數的對象是一個Stream,也不會再建立一個新的Stream,而是將原Stream的元素取代爲轉換的Stream。 List<User> users = UserUtil.getUsers(6); users.stream() .flatMap(s -> s.getInterests().stream()) .forEach(System.out::println); Stream.of("a", "b", "c").flatMap(s -> Stream.of(s.toUpperCase())); //peek 生成一個包含原Stream的全部元素的新Stream,同時會提供一個消費函數(Consumer實例) Stream.of("a", "b", "c") //優先執行 .peek(s -> System.out.println("peek:" + s)) .forEach(System.out::println); //skip 將過濾掉原Stream中的前N個元素,返回剩下的元素所組成的新Stream。 // 若是原Stream的元素個數大於N,將返回原Stream的後的元素所組成的新Stream; // 若是原Stream的元素個數小於或等於N,將返回一個空Stream。 Stream.of("a", "b", "c").skip(2) .forEach(System.out::println); // sorted方法將對原Stream進行排序,返回一個有序列的新Stream。sorterd有兩種變體sorted(),sorted(Comparator), // 前者將默認使用Object.equals(Object)進行排序,然後者接受一個自定義排序規則函數(Comparator),可自定義進行排序。 Stream.of(5, 6, 3, 9, 1) .sorted() .forEach(System.out::println); System.out.println("+++++++++++++++++++++++++++++++"); Stream.of(5, 6, 3, 9, 1) .sorted(new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { //asc return o1 - o2; } }) .forEach(System.out::println); Stream.of(5, 6, 3, 9, 1) //desc .sorted(((o1, o2) -> o2 - o1)) .forEach(System.out::println); System.out.println("+++++++++++++++++++++++++++++++"); // count 將返回Stream中元素的個數。 long count = Stream.of(1, 2, 3, 4, 5).count(); System.out.println("count:" + count); // forEach 用於遍歷Stream中的所元素,避免了使用for循環,讓代碼更簡潔,邏輯更清晰。 Stream.of("a", "b", "c").forEach(System.out::println); // forEachOrdered 與forEach相似,都是遍歷Stream中的全部元素, // 不一樣的是,若是該Stream預先設定了順序,會按照預先設定的順序執行(Stream是無序的),默認爲元素插入的順序。 Stream.of(5,2,1,4,3) .forEachOrdered(integer -> System.out.println("integer:" + integer) ); // max 根據指定的Comparator,返回一個Optional,該Optional中的value值就是Stream中最大的元素。 Optional<Integer> max = Stream.of(5, 2, 2, 3, 4, 8) .max((o1, o2) -> o2 - o1); // Optional<Integer> max3 = Stream.of(1, 2, 3, 4, 5) // .max(Comparator.comparingInt(x -> x)); Optional<Integer> max3 = Stream.of(1, 2, 3, 4, 5) .max((o1, o2) -> o1 - o2); int max2 = Stream.of(1, 2, 3, 4, 5) .mapToInt(x -> x).max().getAsInt(); System.out.println("max = " + max.get() + " max2 = " + max2 + " max3 = " + max3.orElse(-1)); UserUtil.getUsers(6).stream() .sorted(Comparator.comparing(User::getName).thenComparing(User::getId)) .forEach(u -> System.out.println(u.getName())); // min 根據指定的Comparator,返回一個Optional,該Optional中的value值就是Stream中最小的元素。 Optional<Integer> min = Stream.of(1, 2, 3, 4, 5) .min((o1, o2) -> o1 - o2); System.out.println("min:" + min.get()); System.out.println("*********************reduce********************"); // reduce // 一、reduce((sum, item) -> { ... }); //返回Optional,由於可能存在爲空的狀況, // 二、reduce(0, (sum, item) -> { ... }); /返回對應類型,不存在爲空的狀況 //無初始值,第一個參數爲stream的第一個元素,第二個參數爲stream的第二個元素,計算的結果賦值給下一輪計算的sum Optional<Integer> optional = Stream.of(1, 2, 3, 4, 5).reduce((sum, item) -> { System.out.println("sum before:" + sum); System.out.println("item:" + item); sum = sum + item; System.out.println("sum after:" + sum); return sum; // return Integer.sum(sum, item); }); //等效 Optional<Integer> optional1 = Stream.of(1, 2, 3, 4, 5).reduce((sum, item) -> Integer.sum(sum, item) ); //等效 Optional<Integer> optional2 = Stream.of(1, 2, 3, 4, 5).reduce(Integer::sum); System.out.println("integer = " + optional.orElse(-1)); System.out.println("*****************************************"); //給定初始值,第一個參數爲初始值,第二個參數爲stream的第一個元素,計算的結果賦值給下一輪計算的sum Integer reduce = Stream.of(1, 2, 3, 4, 5).reduce(5, (sum, item) -> { System.out.println("sum2 before:" + sum); System.out.println("item:" + item); sum = sum + item; System.out.println("sum2 after:" + sum); return sum; }); //等效 Integer reduce2 = Stream.of(1, 2, 3, 4, 5).reduce(0, (sum, item) -> Integer.sum(sum, item) ); //等效 Integer reduce3 = Stream.of(1, 2, 3, 4, 5).reduce(0, Integer::sum); System.out.println("reduce = " + reduce); System.out.println("*********************collect********************"); List<Integer> toList = Stream.of(1, 2, 3, 4) .collect(Collectors.toList()); List<Integer> toList2 = Stream.of(1, 2, 3, 4) .collect(Collectors.toCollection(ArrayList::new)); System.out.println("toList: " + toList); Set<Integer> toSet = Stream.of(1, 2, 3, 4) .collect(Collectors.toSet()); Set<Integer> toSet2 = Stream.of(1, 2, 3, 4) .collect(Collectors.toCollection(() -> new TreeSet())); System.out.println("toSet: " + toSet); //(value1, value2) -> value1 用前面的value覆蓋後面的value,保持不變 List<User> userList = UserUtil.getUsers(5); userList.add(new User(2, "fjw")); Map<Integer, String> toMap = userList.stream() .collect(Collectors.toMap(User::getId, User::getName, (value1, value2) -> value1)); System.out.println("(value1, value2) -> value1"); toMap.forEach((k, v) -> System.out.println(k + "-" + v)); // 對value值進行了限定不能爲null,不然拋出空指針異常 // userList.add(new User(3, null)); //(value1, value2) -> value2 用後面的value覆蓋前面的value Map<Integer, String> toMap2 = userList.stream() .collect(Collectors.toMap(User::getId, User::getName, (value1, value2) -> value2)); System.out.println("(value1, value2) -> value2"); toMap2.forEach((k, v) -> System.out.println(k + "-" + v)); // 解決value值爲null方式 userList.add(new User(4, null)); Map<Integer, String> toMap3 = userList.stream() .collect(HashMap::new, (m, u) -> m.put(u.getId(), u.getName()), HashMap::putAll); toMap3.forEach((k, v) -> System.out.println(k + "-" + v)); Optional<Integer> maxBy = Stream.of(1, 2, 3, 4) .collect(Collectors.maxBy(Comparator.comparingInt(o -> o))); System.out.println("maxBy:" + maxBy.get()); Long counting = Stream.of(1, 2, 3, 4) .collect(Collectors.counting()); System.out.println("counting:" +counting); //分割數據塊 Map<Boolean, List<Integer>> partitioningBy = Stream.of(1, 2, 3, 4, 5) .collect(Collectors.partitioningBy(item -> item > 3)); //partitioningBy : {false=[1, 2, 3], true=[4, 5]} System.out.println("partitioningBy : " + partitioningBy); Map<Boolean, Long> collect = Stream.of(1, 2, 3, 4) .collect(Collectors.partitioningBy(item -> item > 3, Collectors.counting())); System.out.println("collect: " + collect); //數據分組 Map<Boolean, List<Integer>> groupingBy = Stream.of(1, 2, 3, 4, 5) .collect(Collectors.groupingBy(item -> item > 3)); //partitioningBy : {false=[1, 2, 3], true=[4, 5]} System.out.println("groupingBy : " + groupingBy); //字符串 String joining = Stream.of("a", "b", "c", "d") .collect(Collectors.joining(",")); System.out.println(joining); String joining2 = Stream.of("a", "b", "c", "d") .collect(Collectors.joining(",", "[", "]")); System.out.println(joining2);