Java 8 中,引入了流(Stream)的概念,利用提供的Stream API,咱們能夠方便的操做集合數據,這種方式很相似於使用SQL對數據庫的操做。java
利用Stream API,首先咱們須要生成流,如下是生成流的經常使用方式(這裏咱們只介紹順序流):數據庫
一、全部繼承自Collection的接口均可以直接轉化爲流:數據結構
List<Integer> l1 = Arrays.asList(1, 2, 3, 4, 5); Stream<Integer> stream = l1.stream(); Map<String,Student> s1 = new HashMap<>(); Stream<Map.Entry<String, Student>> stream1 = s1.entrySet().stream();
二、利用Arrays類中的stream()方法:app
Integer[] i1 = new Integer[]{1,2,3,4,5}; Stream<Integer> stream = Arrays.stream(i1);
三、使用Stream類中的靜態方法:ide
Stream<Integer> stream = Stream.of(1,2,3,4,5);
四、利用BufferedReader讀取文本中的內容轉化爲流:函數
BufferedReader reader = new BufferedReader(new FileReader("D:\\stream.txt")); Stream<String> stream = reader.lines();
咱們常常使用的仍是方式1,將集合轉化爲流。線程
流的主要操做有篩選/切片/查找/匹配/映射/歸約code
操做流的方法分爲兩類,一類是惰性求值,一類是及早求值;對象
惰性求值並非馬上執行的,而是將求值的過程記錄下,在進行及早求值的時候,纔會按順序執行前面的惰性求值,通常的執行過程以下:排序
Stream.惰性求值.惰性求值. ... .惰性求值.及早求值
區分是惰性求值仍是及早求值,能夠經過方法的返回值來判斷,返回值時Stream類型的就是惰性求值
在介紹如何經過Stream API對流進行操做前,咱們首先了解下函數式接口的相關知識(後面介紹的Stream API中會使用到這些函數式接口),這裏只做簡單介紹。
所謂函數式接口,是指只含有一個抽象方法的接口,通常加@FunctionalInterface註解加以標註,函數式接口能夠被隱式地轉化爲Lamada表達式;
這裏咱們主要介紹了java.util.function包下的四個經常使用的函數式接口:
接口 | 方法 | 簡介 |
---|---|---|
Consumer | void accept(T t) | 消費接口,接收一個T類型的對象 |
Supplier | T get() | 供給接口,返回一個T類型的對象 |
Function | R apply(T t) | 映射接口,接收一個T類類型的對象轉換爲R類型的對象 |
Predicate | boolean test(T t) | 判斷接口,判斷一個T類型的對象是否知足某個條件,返回一個boolean類型 |
首先咱們將java.util.stream.stream類中的API進行分類:
方法類型 | 方法名 | |
---|---|---|
惰性求值 | 無狀態 | filter();map();mapToInt();mapToLong();mapToDouble;flatMap()... |
有狀態 | distinct();sorted();skip();limit()... | |
及早求值 | 非短路操做 | reduce();forEach();collect();min();max();count()... |
短路操做 | anyMatch();allMatch()... |
無狀態:元素的處理是獨立的,不受前面元素的影響; 有狀態:元素的處理不是獨立的,受到前面元素的影響; 非短路:必須處理完全部元素才能獲得結果; 短路:不須要處理完全部元素就能獲得結果;
下面咱們介紹下經常使用的Stream方法
一、filter()
方法定義:
Stream<T> filter(Predicate<? super T> predicate)
方法介紹:
用於對數據進行過濾,篩選出符合條件的數據; 接收一個Predicate的函數接口,用於進行條件的過濾;返回符合條件的數據組成的一個新的流;
代碼示例:
List<Integer> l1 = Arrays.asList(1,2,3,4,5); //過濾出大於2的元素 Stream<Integer> integerStream = l1.stream().filter(s -> s > 2);
二、map()
方法定義:
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
方法介紹:
對數據進行處理,將T類型的對象轉化爲R類型的對象,簡單來講就是對流中的數據進行同一項操做; 接收一個Function的函數接口,用於對數據處理;返回R類型的數據組成的一個新的流;
代碼示例:
List<String> l2 = Arrays.asList("A", "B", "C"); //將每一個元素所有轉化爲小寫 Stream<String> stringStream = l2.stream().map(s -> s.toLowerCase());
注:mapToInt(),mapToLong(),mapToDouble(),這是java針對基本數據類型封裝出對應的流,這裏就不在介紹;
三、flatMap()
方法定義:
<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper)
方法介紹:
將流中的每個元素轉化爲一個流,而後將轉化的一個個流組合成一個新的流; 接收一個Function的函數接口,用於對數據處理;返回R類型的數據組成的一個新的流;
代碼示例:
List<List<Integer>> l3 = Arrays.asList(Arrays.asList(1,2,5,4,7),Arrays.asList(4,8,6,9,4)); //將l3中的每一個元素(List),轉化爲一個個流,而後組合成一個新的流 Stream<Integer> integerStream1 = l3.stream().flatMap(s -> s.stream());
四、sorted()
方法定義:
Stream<T> sorted(); Stream<T> sorted(Comparator<? super T> comparator);
方法介紹:
將流中的數據進行排序,而後排序後的數據組合成一個新的流; 無參的方法,默認按照升序升序進行排列; 有參的方法,須要傳入Comparator接口的一個實現類,按照該實現進行排序操做;
代碼示例:
List<Integer> l1 = Arrays.asList(2,1,4,5); //升序排列 Stream<Integer> sorted = l1.stream().sorted(); //降序排列 Stream<Integer> sorted1 = l1.stream().sorted((t1, t2) -> { return t1 < t2 ? 1 : t1 == t2 ? 0 : -1; });
五、reduce()
方法定義:
Optional<T> reduce(BinaryOperator<T> accumulator); T reduce(T identity, BinaryOperator<T> accumulator); <U> U reduce(U identity,BiFunction<U, ? super T, U> accumulator,BinaryOperator<U> combiner);
方法介紹:
將流中的數據進行規約,返回規約後的數據; Optional<T> reduce(BinaryOperator<T> accumulator):第一次執行時,accumulator函數的第一個參數爲流中的第一個元素,第二個參數爲流中元素的第二個元素;第二次執行時,第一個參數爲第一次函數執行的結果,第二個參數爲流中的第三個元素;依次類推。 T reduce(T identity, BinaryOperator<T> accumulator):流程跟上面同樣,只是第一次執行時,accumulator函數的第一個參數爲identity,而第二個參數爲流中的第一個元素。 <U> U reduce(U identity,BiFunction<U, ? super T, U> accumulator,BinaryOperator<U> combiner):在串行流(stream)中,第三個參數combiner不會起做用,該方法跟第二個方法同樣。在並行流(parallelStream)中,道流被fork join出多個線程進行執行,此時每一個線程的執行流程就跟第二個方法reduce(identity,accumulator)同樣,而第三個參數combiner函數,則是對每一個線程的執行結果進行規約,最終返回結果。
代碼示例:
List<Integer> l1 = Arrays.asList(2,1,4,5); //求和,返回的是Optional對象 Optional<Integer> reduce = l1.stream().reduce((t1, t2) -> t1 + t2); //求和返回的是Integer對象 Integer reduce1 = l1.stream().reduce(0, (t1, t2) -> t1 + t2); //每一個單獨的並行流進行求和操做,並行流輸出的結果進行相乘操做輸出最終結果 Integer reduce2 = l1.parallelStream().reduce(0, (t1, t2) -> t1 + t2, (s1, s2) -> s1 * s2);
六、collect()
方法定義:
<R, A> R collect(Collector<? super T, A, R> collector);
<R> R collect(Supplier<R> supplier,BiConsumer<R, ? super T> accumulator,BiConsumer<R, R> combiner);
方法介紹:
將流中的數據轉化爲一個新的數據結構; <R, A> R collect(Collector<? super T, A, R> collector):傳入一個想要輸出的集合類型,通常經過Collectors建立集合類型 <R> R collect(Supplier<R> supplier,BiConsumer<R, ? super T> accumulator,BiConsumer<R, R> combiner):第一個參數supplier爲結果存放容器,第二個參數accumulator爲結果如何添加到容器的操做,第三個參數combiner則爲多個容器的聚合策略
代碼示例:
//將流轉化爲List數據結構 List<Integer> collect1 = Stream.of(1, 2, 3).collect(Collectors.toList()); //將流轉化爲Map結構 Student student1 = new Student("xiaoni", "male", 15); Student student2 = new Student("xiaohua", "female", 20); List<Student> l1 = new ArrayList<>(); l1.add(student1); l1.add(student2); HashMap<String, Student> collect = l1.stream() .collect(() -> new HashMap<String, Student>(), (h, v) -> {h.put(v.getName(),v);}, HashMap::putAll);
七、綜合訓練
public static void main(String[] args) throws FileNotFoundException { Student s1 = new Student("xiaoni", "male", 18); Student s2 = new Student("xiaohua", "female", 20); Student s3 = new Student("xiaodong", "male", 19); Student s4 = new Student("xiaoben", "male", 24); Student s5 = new Student("xiaoyun", "female", 23); Student s6 = new Student("xiaojing", "female", 20); List<Student> l1 = new ArrayList<>(); l1.add(s1); l1.add(s2); l1.add(s3); l1.add(s4); l1.add(s5); //統計男生的數量 long count = l1.stream().filter(s -> "male".equals(s.getSex())).count(); System.out.println("男生數量:"+count); //按年齡從大到小進行排序 List<Student> collect = l1.stream() .sorted((k1,k2) -> { int a1 = k1.getAge(); int a2 = k2.getAge(); return a1 < a2 ? 1 : a1 == a2 ? 0 : -1; }) .collect(Collectors.toList()); System.out.println("按年齡排序:" + collect); //求全部學生的年齡之和 long count1 = l1.stream().mapToInt(s -> s.getAge()).sum(); System.out.println("年齡之和:" + count1); //將數據存放到map中,學生姓名做爲key值 Map<String, Student> collect1 = l1.stream().collect(Collectors.toMap(Student::getName, s -> s)); System.out.println("map中的學生:"+collect1); List<Integer> l2 = Arrays.asList(1,5,4,7,9,2,4,5,1); //輸出最小的三個數字,不包含重複的數字 List<Integer> l3 = l2.stream().distinct().sorted(Integer::compareTo).limit(3).collect(Collectors.toList()); System.out.println("最小的三個數:"+l3); //輸出第二小的數字 List<Integer> l4 = l2.stream().distinct().sorted(Integer::compareTo).skip(1).limit(1).collect(Collectors.toList()); System.out.println("第二小的數字:"+l4); }