開始以前先摘抄一些概念,java
流的操做類型分爲兩種:數組
Intermediate:一個流能夠後面跟隨零個或多個 intermediate 操做。其目的主要是打開流,作出某種程度的數據映射/過濾,而後返回一個新的流,交給下一個操做使用。這類操做都是惰性化的(lazy),就是說,僅僅調用到這類方法,並無真正開始流的遍歷。安全
Terminal:一個流只能有一個 terminal 操做,當這個操做執行後,流就會被,沒法再被操做。因此這一定是流的最後一個操做。Terminal 操做的執行,纔會真正開始流的遍歷,而且會生成一個結果,或者一個 side effect。app
short-circuitingide
對於一個無限大(infinite/unbounded)的 Stream,返回有限的新的Stream或者能在有限的時間內返回結果性能
經過以上的概念,能夠歸類以下的方法ui
Intermediate:this
map (mapToInt, flatMap 等)、 filter、 distinct、 sorted、 peek、 limit、 skip、 parallel、 sequential、 unordered線程
Terminal:code
forEach、 forEachOrdered、 toArray、 reduce、 collect、 min、 max、 count、 anyMatch、 allMatch、 noneMatch、 findFirst、 findAny、 iterator
Short-circuiting:
anyMatch、 allMatch、 noneMatch、 findFirst、 findAny、 limit
// 1. 從Collection中生成 List list = new ArrayList<String>(){{add("A");add("B");}}; Stream<String> stream = list.stream(); // 2. 下面是從數組生成 int[] arr = new int[]{1,2,3}; IntStream stream = Arrays.stream(arr); // or Stream.of(1,2,3)
// 從BufferedReader中生成 java.io.BufferedReader.lines() // 經過一些靜態的方法 java.util.stream.IntStream.range() java.nio.file.Files.walk() java.nio.file.Files.lines()
自定義
經過java.util.Spliterator 能夠本身構建,這裏就不展開了,有興趣的能夠參考下面兩篇文章
http://ifeve.com/java8-stream-%E4%B8%ADspliterator%E7%9A%84%E4%BD%BF%E7%94%A8%E4%BA%8C/
先構建一個對象,下面全部的方法會用到這個基礎對象,以及構造一個簡單的persons,供下文使用
class Person{ private String name; private Integer age; // 省略getter,setter方法 public Person(String name, Integer age){ this.name = name; this.age = age; } }
List<Person> persons = new ArrayList<>(); persons.add(new Person("jack",17)); persons.add(new Person("rose",18)); persons.add(new Person("小明",17));
map會對流中的每一個元素應用map中的lambda表達式
List<String> result = persons.stream().map( a -> a.getName().toUpperCase()).collect(Collectors.toList()); // result中的元素爲 [JACK, ROSE, 小明]
上面的意思是取流中的每一個元素(這裏就是Person),把name屬性轉爲大寫,最後的collect(Collectors.toList())就是上面說的Terminal操做,所以咱們能夠獲取到一個List
上面是一對一的操做,若是是一對多的,則可使用flatMap
Stream<List<Integer>> inputStream = Stream.of( Arrays.asList(1), Arrays.asList(2, 3), Arrays.asList(4, 5, 6) ); Stream<Integer> outputStream = inputStream.flatMap((childList) -> childList.stream());
flatMap抽取出底層的元素放在一塊兒,就是上面的例子中把三個list中的元素所有抽取出來,最後返回一個數字流
List<Person> collect = persons.stream() .filter(a -> a.getAge() > 17).collect(Collectors.toList()); // 過濾出流中全部age > 17的元素,返回一個新的流
filter接受一個Predicate(謂詞)參數,返回符合爲true的元素
顧名思義就是去重的操
List<Integer> integers = Arrays.asList(1, 1, 1); List<Integer> result = integers.stream().distinct().collect(Collectors.toList()); // 最終result裏面的元素只有一個[1]
須要注意的是,每一個元素去重基於的 Object#equals(Object) 方法,在一些場合使用Collection中的removeIf()默認方法更加合適
Stream提供了 foreach 方法來遍歷流中的每一個數據
persons.stream().forEach(a -> System.out.println(a.getName())); // 更簡單的寫法以下,由於List接口中有了一個forEach默認方法,關於接口默認方法這裏就不闡述了 persons.forEach(a -> System.out.println(a.getName()));
sorted會對流中的元素作排序,默認是從小大到的順序
List<Person> result = persons.stream().sorted(Comparator.comparing(Person::getAge)).collect(Collectors.toList());
若是能夠Stream 進行各種 map、filter、limit、skip 甚至 distinct 來減小元素數量後,再排序,這序明顯縮短程序執行時間
max/min操做相似,不過他們各自返回最大值和最小值,若是是對象都是int,long,double類型,可使用IntStream、LongStream、DoubleStream,避免拆箱裝箱的性能消耗。這個時候可使用以下操做
OptionalLong max = persons.stream().mapToLong(Person::getAge).max(); System.out.println(max.getAsLong()); //輸出 18
以前咱們說過流只能被terminal 操做一次,若是有相似的場景須要屢次terminal 操做,peek能夠達到目的
// peek方法方法會使用一個Consumer消費流中的元素,可是返回的流仍是包含原來的流中的元素。 OptionalLong max = persons.stream().peek(a -> System.out.println(a.getName())).mapToLong(Person::getAge).max(); System.out.println(max.getAsLong());
// 流中全部的元素都知足謂詞中的條件才返回true public boolean allMatch(Predicate<? super T> predicate) // 流中全部的元素有一個知足謂詞中的條件就返回true public boolean anyMatch(Predicate<? super T> predicate) // 流中全部的元素都不知足謂詞中的條件才返回true public boolean noneMatch(Predicate<? super T> predicate)
reduce方法返回單個的結果值,而且reduce操做每處理一個元素老是建立一個新值。reduce主要有以下三個方法
// 最經常使用的,identity參數是初始值,accumulator是累加器,其方法簽名是 apply(T t,U u),累加的值會被賦值給下次執行方法的第一個參數,也就是t T reduce(T identity, BinaryOperator<T> accumulator); // 沒有初始值,返回的是一個Optional,由於null是不安全的 Optional<T> reduce(BinaryOperator<T> accumulator); // 第三個參數是使用並行流(parallelStream)時,合併每一個線程的操做 <U> U reduce(U identity, BiFunction<U, ? super T, U> accumulator, BinaryOperator<U> combiner);
因此經常使用的方法有average, sum, min, max, count,使用reduce方法均可實現。
// 實現加法 Integer result = Stream.of(1, 2, 3).stream().reduce(0, (a, b) -> a + b); // result:6 // 實現減法 Integer result = Stream.of(1, 2, 3).reduce(10, (a, b) -> a - b); // result:10 - 6 = 4
collect能夠把流收集起來,能夠是一個List,Map以及分組等
List<Integer> result = Stream.of(1, 2, 3).collect(ArrayList::new, ArrayList::add, List::addAll); // result: [1,2,3]
代碼看起來並不清真,也不容易理解。所以,還有另外的簡便寫法,下面會提到
Collectors實現了Collector接口,提供了不少有用的方法
List<Integer> result = Stream.of(1, 2, 3).collect(ArrayList::new, ArrayList::add, List::addAll); // 能夠簡寫爲 List<Integer> result = Stream.of(1, 2, 3).collect(Collectors.toList()); // 若是須要去重,可使用toSet Set<Integer> result = Stream.of(1, 1, 3).collect(Collectors.toSet());
// Function.identity() 的做用等同於 a -> a,即輸入等於輸出 Map<Integer, Person> collect = persons.stream().collect(Collectors.toMap(Person::getAge, Function.identity())); // 這裏就要有坑了,若是key相同的話,會拋出異常,能夠經過指定第三個參數指定合併方式 Map<Integer, Person> collect = persons.stream().collect(Collectors.toMap(Person::getAge, Function.identity(),(a,b) -> a)); // 這裏咱們指定了若是有衝突,取以前的那個
覺得這樣就沒有坑了麼?不對,若是在value爲null的時候,還會拋出 NPE的異常,HashMap中的merge代碼中有以下這一行。
因此,最好不要使用這個方法,使用以下的寫法代替
Map<Integer,Person> map = new HashMap<>(); persons.forEach(a -> map.put(a.getAge(),a));