JAVA流式計算

JAVA流式計算

流的簡單介紹

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類型
使用Stream API操做流

首先咱們將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);

    }
相關文章
相關標籤/搜索