Java8 - 使用Stream APIjava
Stream represents a sequence of objects from a source, which supports aggregate operations(多種操做). Following are the characteristics(特徵) of a Stream −api
Sequence of elements − A stream provides a set of elements of specific type in a sequential manner. A stream gets/computes elements on demand. It never stores the elements.數組
Source − Stream takes Collections, Arrays, or I/O resources as input source.ide
Aggregate operations − Stream supports aggregate operations(多種操做) like filter, map, limit, reduce, find, match, and so on.函數
Pipelining − Most of the stream operations return stream itself so that their result can be pipelined. These operations are called intermediate operations and their function is to take input, process them, and return output to the target. collect() method is a terminal operation(終端操做) which is normally present at the end of the pipelining operation to mark the end of the stream.ui
Automatic iterations − Stream operations do the iterations internally over the source elements provided, in contrast to Collections where explicit iteration is required.this
流有多種建立方式,好比最多見的經過集合,數組spa
package com.common.stream; import java.util.Arrays; import java.util.List; import java.util.function.Supplier; import java.util.stream.Stream; public class CreateStreamTest { private static final List<Dish> menu = Arrays.asList( new Dish("pork", 800, Dish.Type.MEAT), new Dish("beef", 700, Dish.Type.MEAT), new Dish("chicken", 400, Dish.Type.MEAT), new Dish("french fries", 530, Dish.Type.OTHER), new Dish("rice", 350, Dish.Type.OTHER), new Dish("season fruit", 120, Dish.Type.OTHER), new Dish("pizza", 550, Dish.Type.OTHER), new Dish("prawns", 300, Dish.Type.FISH), new Dish("salmon", 450, Dish.Type.FISH)); public static void main(String[] args) { //獲取一個stream 流 Stream<Dish> stream = menu.stream(); stream.peek(x -> System.out.println("from stream: " + x)). filter((x) -> x.getCalories() > 500). peek(x -> System.out.println("after filter: " + x)). forEach(System.out::println); //獲取一個並行的流 Stream parallelStream = menu.parallelStream(); } }
上面這段代碼輸出打印,code
from stream: Dish{name='pork', calories=800} after filter: Dish{name='pork', calories=800} Dish{name='pork', calories=800} from stream: Dish{name='beef', calories=700} after filter: Dish{name='beef', calories=700} Dish{name='beef', calories=700} from stream: Dish{name='chicken', calories=400} from stream: Dish{name='french fries', calories=530} after filter: Dish{name='french fries', calories=530} Dish{name='french fries', calories=530} from stream: Dish{name='rice', calories=350} from stream: Dish{name='season fruit', calories=120} from stream: Dish{name='pizza', calories=550} after filter: Dish{name='pizza', calories=550} Dish{name='pizza', calories=550} from stream: Dish{name='prawns', calories=300} from stream: Dish{name='salmon', calories=450}
從輸出來看,Stream 流至關於一個迭代器,依次遍歷全部的元素。orm
可使用靜態方法Arrays.stream從數組建立一個流。它接受一個數組做爲參數。
String[] arrayOfWords = {"Goodbye", "World"}; Stream<String> streamOfwords = Arrays.stream(arrayOfWords); int[] numbers = {2, 3, 5, 7, 11, 13}; int sum = Arrays.stream(numbers).sum();
可使用靜態方法Stream.of,經過顯式值建立一個流。它能夠接受任意數量的參數。例 如,如下代碼直接使用Stream.of建立了一個字符串流。而後,能夠將字符串轉換爲大寫,再 一個個打印出來:
Stream.of("hello", "world").map(String::toUpperCase).forEach(System.out::println);
Java中用於處理文件等I/O操做的NIO API(非阻塞 I/O)已更新,以便利用Stream API。 java.nio.file.Files中的不少靜態方法都會返回一個流。例如,一個頗有用的方法是 Files.lines,它會返回一個由指定文件中的各行構成的字符串流。
long uniqueWords = 0; try (Stream<String> lines = Files.lines(Paths.get("/Users/flyme/workspace/showcase/common/src/main/java/data.txt"), Charset.defaultCharset())) { uniqueWords = lines.flatMap(line -> Arrays.stream(line.split(" "))).distinct() .count(); System.out.println(uniqueWords); } catch (IOException e) { e.printStackTrace(); }
Stream API提供了兩個靜態方法來從函數生成流:Stream.iterate和Stream.generate。 這兩個操做能夠建立所謂的無限流:不像從定集合建立的流那樣有定大小的流。由iterate 2 和generate產生的流會用給定的函數按需建立值,所以能夠無無盡地計算下去!通常來講, 應該使用limit(n)來對這種流加以限制,以免打印無窮多個值。
package com.common.stream; import java.util.function.Supplier; import java.util.stream.Stream; public class StreamGenerateTest { static class NaturalSupplier implements Supplier<Long> { long value = 0; @Override public Long get() { this.value = this.value + 1; return this.value; } } public static void main(String[] args) { // iterate方法接受一個初始值(在這裏是0),還有一個依次應用在每一個產生的新值上的 Lambda(UnaryOperator<t>類型)。 // 這裏,咱們使用Lambda n -> n + 2,返回的是前一個元 素加上2。 // 所以,iterate方法生成了一個全部正偶數的流:流的第一個元素是初始值0。 // 而後加 上2來生成新的值2,再加上2來獲得新的值4,以此類推。 // 這種iterate操做基本上是順序的, 由於結果取決於前一次應用。 // 請注意,此操做將生成一個無限流——這個流沒有結,由於值是 按需計算的,能夠遠計算下去。 // 咱們說這個流是無界的。正如咱們前面所討論的,這是流和集 合之間的一個關鍵區別。咱們使用limit方法來顯式限制流的大小。 // 這裏只選擇了前10個偶數。 8 而後能夠調用forEach終端操做來消費流,並分別打印每一個元素。 Stream.iterate(0, n -> n + 2) .limit(10) .forEach(System.out::println); //建立一個無窮數列的stream Stream<Long> natural = Stream.generate(new NaturalSupplier()); //使用limit 方法截斷流 natural.peek(n -> { System.out.println("from stream:" + n); }).map((x) -> x * x).limit(10).forEach(System.out::println); } }
一是集合類持有的全部元素都是存儲在內存中的,很是巨大的集合類會佔用大量的內存,而Stream的元素倒是在訪問的時候才被計算出來,這種「延遲計算」的特性有點相似Clojure的lazy-seq,佔用內存不多。
二是集合能夠遍歷屢次,而流只能遍歷一次,遍歷完以後,咱們就說這個流已經被消費掉了。 你能夠從原始數據源那裏再得到一個新的流來從新遍歷一遍,就像迭代器同樣(這裏假設它是集 合之類的可重複的源,若是是I/O通道就沒戲了)。
三是集合類的迭代邏輯是調用者負責,一般是for循環,這稱爲外部迭代;而Stream的迭代是隱含在對Stream的各類操做中,例如map(),稱爲內部迭代。
示例一,經過Stream建立一個無窮大小的天然數集合,使用 Supplier 能夠建立一個無窮大小的 Stream。若是用集合是作不到的。
package com.common.stream; import java.util.function.Supplier; import java.util.stream.Stream; public class CreateStreamTest { static class NaturalSupplier implements Supplier<Long> { long value = 0; @Override public Long get() { this.value = this.value + 1; return this.value; } } public static void main(String[] args) { //建立一個無窮數列的stream Stream<Long> natural = Stream.generate(new NaturalSupplier()); //使用limit 方法截斷流 natural.peek(n -> { System.out.println("from stream:" + n); }).map((x) -> x * x).limit(10).forEach(System.out::println); } }
示例二,Stream 只能遍歷一次,若是遍歷屢次,會報錯
package com.common.stream; import java.util.Arrays; import java.util.List; import java.util.stream.Stream; public class StreamIterationTest { private static final List<Dish> menu = Arrays.asList( new Dish("pork", 800, Dish.Type.MEAT), new Dish("beef", 700, Dish.Type.MEAT), new Dish("chicken", 400, Dish.Type.MEAT), new Dish("french fries", 530, Dish.Type.OTHER), new Dish("rice", 350, Dish.Type.OTHER), new Dish("season fruit", 120, Dish.Type.OTHER), new Dish("pizza", 550, Dish.Type.OTHER), new Dish("prawns", 300, Dish.Type.FISH), new Dish("salmon", 450, Dish.Type.FISH)); public static void main(String[] args) { Stream<Dish> stream = menu.stream(); stream.peek(x -> System.out.println("from stream: " + x)). filter((x) -> x.getCalories() > 500). peek(x -> System.out.println("after filter: " + x)). forEach(System.out::println); stream.forEach(System.out::println); } }
輸出,
Exception in thread "main" java.lang.IllegalStateException: stream has already been operated upon or closed
at java.util.stream.AbstractPipeline.sourceStageSpliterator(AbstractPipeline.java:279)
at java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:580)
at com.common.stream.StreamIterationTest.main(StreamIterationTest.java:27)
示例三,集合和Stream遍歷的不一樣
package com.common.stream; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; public class StreamIterationTest { private static final List<Dish> menu = Arrays.asList( new Dish("pork", 800, Dish.Type.MEAT), new Dish("beef", 700, Dish.Type.MEAT), new Dish("chicken", 400, Dish.Type.MEAT), new Dish("french fries", 530, Dish.Type.OTHER), new Dish("rice", 350, Dish.Type.OTHER), new Dish("season fruit", 120, Dish.Type.OTHER), new Dish("pizza", 550, Dish.Type.OTHER), new Dish("prawns", 300, Dish.Type.FISH), new Dish("salmon", 450, Dish.Type.FISH)); public static void main(String[] args) { List<String> names = new ArrayList<>(); // for each 循環外部迭代 for (Dish dish : menu) { names.add(dish.getName()); } // 外部迭代方式 names = new ArrayList<>(); Iterator<Dish> iterator = menu.iterator(); while (iterator.hasNext()) { Dish d = iterator.next(); names.add(d.getName()); } names.clear(); // 使用stream 的內部迭代 Stream<Dish> stream = menu.stream(); stream.peek(x -> System.out.println("from stream: " + x)). map((x) -> x.getName()).collect(Collectors.toList()); } }
參考:https://www.tutorialspoint.com/java8/java8_streams.htm
http://www.infoq.com/cn/articles/java8-new-features-new-stream-api
對stream的操做分爲爲兩類,中間操做(intermediate operations)和結束操做(terminal operations),兩者特色是:
看示例代碼,
package com.common.stream; import java.util.Arrays; import java.util.List; public class StreamOperateTest { private static final List<Dish> menu = Arrays.asList( new Dish("pork", 800, Dish.Type.MEAT), new Dish("beef", 700, Dish.Type.MEAT), new Dish("chicken", 400, Dish.Type.MEAT), new Dish("french fries", 530, Dish.Type.OTHER), new Dish("rice", 350, Dish.Type.OTHER), new Dish("season fruit", 120, Dish.Type.OTHER), new Dish("pizza", 550, Dish.Type.OTHER), new Dish("prawns", 300, Dish.Type.FISH), new Dish("salmon", 450, Dish.Type.FISH)); public static void main(String[] args) { // filter 過濾操做 menu.stream().filter(n -> n.getCalories() > 500 ).forEach(System.out::println); // distinct 去重操做 Arrays.asList(1, 2, 1, 3, 3, 2, 4).stream().filter(i -> i % 2 == 0).distinct().forEach(System.out::println); // limit 截斷流的操做 只保留前兩個元素 menu.stream().filter(n -> n.getCalories() > 200 ).limit(2).forEach(System.out::println); // skip 跳過操做 跳過前兩個元素,返回剩下的元素 menu.stream().filter(n -> n.getCalories() > 200 ).skip(2).forEach(System.out::println); } }
好比map和flatMap方法,能夠把一個元素映射成另外一個元素。如下示例對比了 map 和 flatMap的區別,
package com.common.stream; import java.util.Arrays; import java.util.List; import java.util.stream.Stream; import static java.util.stream.Collectors.toList; public class StreamMapOperateTest { public static void main(String[] args) { String[] arrayOfWords = {"Hello", "World", "Bye"}; List<Stream<String>> res = Arrays.stream(arrayOfWords) // 把每一個單詞映射成一個數組,好比 "Hello" -> ['H','e','l','l','o'] .map(word -> word.split("")) // 把每一個數組映射成一個Stream<String>, 好比 ['H','e','l','l','o'] -> Stream<String> // 效果是 [['H','e','l','l','o'],['W','o','r','l','d'],['B','y','e']] -> [Stream<String>,Stream<String>,Stream<String>] .map(Arrays::stream) .distinct() .collect(toList()); res.forEach(System.out::println); /** * 要理解什麼是扁平化流,請考慮像[ [1,2,3],[4,5,6],[7,8,9] ]這樣的結構,它具備「兩個層次」。 * 扁平化意味着將其轉變爲「一級」結構: [ 1,2,3,4,5,6,7,8,9 ] 。 */ List<String> resStr = Arrays.stream(arrayOfWords) .map(word -> word.split("")) // 效果是二維數組 到 一維數組 // [['H','e','l','l','o'],['W','o','r','l','d'],['B','y','e']] -> ['H','e','l','l','o','W','o','r','l','d','B','y','e'] .flatMap(Arrays::stream) .peek(n -> { System.out.println("after flatMap : " + n); }) .distinct() .collect(toList()); resStr.forEach(System.out::println); } }
package com.common.stream; import java.util.Arrays; import java.util.List; public class StreamFindTest { private static final List<Dish> menu = Arrays.asList( new Dish("pork", 800, Dish.Type.MEAT), new Dish("beef", 700, Dish.Type.MEAT), new Dish("chicken", 400, Dish.Type.MEAT), new Dish("french fries", 530, Dish.Type.OTHER), new Dish("rice", 350, Dish.Type.OTHER), new Dish("season fruit", 120, Dish.Type.OTHER), new Dish("pizza", 550, Dish.Type.OTHER), new Dish("prawns", 300, Dish.Type.FISH), new Dish("salmon", 450, Dish.Type.FISH)); public static void main(String[] args) { // anyMatch 操做 if (menu.stream().anyMatch(x -> x.getCalories() > 500)) { System.out.println("anyMatch...."); } // allMatch if (menu.stream().allMatch(x -> x.getCalories() < 1000)) { System.out.println("allMatch...."); } // noneMatch if (menu.stream().noneMatch(x -> x.getCalories() >= 1000)) { System.out.println("noneMatch...."); } // findAny 方法返回 Optional 對象 // findAny方法將返回當前流中的任意元素。 menu.stream().filter(x -> x.getCalories() > 500).findAny().ifPresent(System.out::println); // 找到第一個元素 menu.stream().filter(x -> x.getCalories() > 500).findFirst().ifPresent(System.out::println); } }
到目前爲止,終端操做都是返回一個boolean(allMatch之類的)、void (forEach)或Optional對象(findAny等),或者使用collect來將流中的全部元素組合成一個List。
歸約操做則是相似求和,將流歸約成一個值。
以下,求全部菜譜的卡路里值,
package com.common.stream; import java.util.Arrays; import java.util.List; public class StreamReduceTest { private static final List<Dish> menu = Arrays.asList( new Dish("pork", 800, Dish.Type.MEAT), new Dish("beef", 700, Dish.Type.MEAT), new Dish("chicken", 400, Dish.Type.MEAT), new Dish("french fries", 530, Dish.Type.OTHER), new Dish("rice", 350, Dish.Type.OTHER), new Dish("season fruit", 120, Dish.Type.OTHER), new Dish("pizza", 550, Dish.Type.OTHER), new Dish("prawns", 300, Dish.Type.FISH), new Dish("salmon", 450, Dish.Type.FISH)); public static void main(String[] args) { // map reduce 操做 int sum = menu.stream().map(Dish::getCalories).reduce(0, (a, b) -> a + b); System.out.println(sum); } }
求最大最小值
// 求最大最小值 Optional<Integer> min = menu.stream().map(Dish::getCalories).reduce(Integer::min); // 求最大最小值 Optional<Integer> max = menu.stream().map(Dish::getCalories).reduce(Integer::max); if (min.isPresent()) { System.out.println(min.get()); } if (max.isPresent()) { System.out.println(max.get()); }
=======END=======