1. 程式人生 > >【java8】持續精進-之流式資料處理

【java8】持續精進-之流式資料處理

流式處理簡介

在我接觸到java8流式處理的時候,我的第一感覺是流式處理讓集合操作變得簡潔了許多,通常我們需要多行程式碼才能完成的操作,藉助於流式處理可以在一行中實現。比如我們希望對一個包含整數的集合中篩選出所有的偶數,並將其封裝成為一個新的List返回,那麼在java8之前,我們需要通過如下程式碼實現:

 /**
     * 準備資料
     *
     * @return
     */
    public static List<Integer> getData() {
        return Arrays.asList(1, 2, 4, 3, 5, 6, 7
, 8, 9, 10, 11, 23); } public static void main(String[] args) { List<Integer> data = getData(); List<Integer> evens = new ArrayList<>(); for (Integer num : data) { if (num % 2 == 0) { evens.add(num); } } }

通過java8的流式處理,我們可以將程式碼簡化為:

    /**
     * 準備資料
     *
     * @return
     */
    public static List<Integer> getData() {
        return Arrays.asList(1, 2, 4, 3, 5, 6, 7, 8, 9, 10, 11, 23);
    }
    public static void main(String[] args) {

        List<Integer> data = getData();

        List<Integer> integerList = data.stream()
                .filter(num -> num % 2
== 0) .collect(Collectors.toList()); }

先簡單解釋一下上面這行語句的含義,stream操作將集合轉換成一個流,filter執行我們自定義的篩選處理,這裡是通過lambda表示式篩選出所有偶數,最後我們通過collect對結果進行封裝處理,並通過Collectors.toList()指定其封裝成為一個List集合返回。

一個流式處理可以分為三個部分:轉換成流、中間操作、終端操作
流->中間操作….->中間操作->終端操作

實戰

1.資料準備

@Data
public class Student {

    /** 學號 */
    private long id;

    /**xingming*/
    private String name;

    private int age;

    /** 年級 */
    private int grade;

    /** 專業 */
    private String major;

    /** 學校 */
    private String school;

    public Student(long id, String name, int age, int grade, String major, String school) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.grade = grade;
        this.major = major;
        this.school = school;
    }
}
 static List<Student> students = new ArrayList<Student>() {
        {
            add(new Student(20160001, "張三", 20, 1, "土木工程", "武漢大學"));
            add(new Student(20160002, "李四", 21, 2, "資訊保安", "武漢大學"));
            add(new Student(20160003, "王五", 22, 3, "經濟管理", "武漢大學"));
            add(new Student(20160004, "趙六", 21, 2, "資訊保安", "武漢大學"));
            add(new Student(20161001, "張琦", 21, 2, "機械與自動化", "華中科技大學"));
            add(new Student(20161002, "阿德", 23, 4, "土木工程", "華中科技大學"));
            add(new Student(20161003, "阿哥", 23, 4, "電腦科學", "華中科技大學"));
            add(new Student(20162001, "阿房", 22, 3, "土木工程", "浙江大學"));
            add(new Student(20162002, "阿飛", 23, 4, "電腦科學", "浙江大學"));
            add(new Student(20163001, "阿德", 24, 5, "土木工程", "南京大學"));
        }
    };```

2.1 過濾操作(filter,distinct,limit,skip)

2.1.1 filter
  在前面的例子中我們已經演示瞭如何使用filter,其定義為:Stream<T> filter(Predicate<? super T> predicate),filter接受一個謂詞Predicate,我們可以通過這個謂詞定義篩選條件,在介紹lambda表示式時我們介紹過Predicate是一個函式式介面,其包含一個test(T t)方法,該方法返回boolean。現在我們希望從集合students中篩選出所有浙江大學的學生,那麼我們可以通過filter來實現,並將篩選操作作為引數傳遞給filter
    List<Student> collect = students.stream()
            .filter(student -> "浙江大學".equals(student.getSchool()))
            .collect(Collectors.toList());
    collect.forEach(System.out::println);
2.1.2 distinct 
    相當於sql中的去重 , 去重資料中的重複數字

    List<Integer>  list = Arrays.asList(1,2,3,3,3,3,3,3,3,35,5,0);

    List<Integer> collect = list.stream().distinct().collect(Collectors.toList());

    collect.forEach(System.out::println);
2.1.3limit
limit操作也類似於SQL語句中的LIMIT關鍵字,不過limit返回包含前n個元素的流,當集合大小小於n時,則返回實際長度,比如下面的例子返回前兩個學生:

List stu = students.stream()
.limit(2)
.collect(Collectors.toList());

    stu.forEach(s-> System.out.println(s));
2.1.4 sorted 
 該操作用於對流中元素進行排序,
 比如我們希望篩選出專業為土木工程的學生,並按年齡從小到大排序,篩選出年齡最小的兩個學生,那麼可以實現為:

    List<Student> stu = students.stream().sorted(Comparator.comparing(Student::getAge))
            .limit(2)
            .collect(Collectors.toList());

    stu.forEach(s-> System.out.println(s));

2.1.5 skip
skip操作與limit操作相反,如同其字面意思一樣,是跳過前n個元素,比如我們希望找出排序在2之後的土木工程專業的學生,那麼可以實現為:

        List<Student> stu = students.stream()
                .filter(student -> "土木工程".equals(student.getMajor()))
                .skip(2)
                .collect(Collectors.toList());

        stu.forEach(s-> System.out.println(s));
2.2 對映
在java8的流式處理中,主要包含兩類對映操作:map和flatMap。
2.2.1 map
舉例說明,假設我們希望篩選出所有專業為電腦科學的學生姓名,那麼我們可以在filter篩選的基礎之上,通過map將學生實體對映成為學生姓名字串,具體實現如下:
   String stu = students.stream()
            .filter(s->s.getMajor().equals("電腦科學"))
            .map(Student::getName)
            .collect(Collectors.joining(", "));


    System.out.println(stu);

除了上面這類基礎的map,java8還提供了mapToDouble(ToDoubleFunction<? super T> mapper),mapToInt(ToIntFunction<? super T> mapper),mapToLong(ToLongFunction<? super T> mapper),這些對映分別返回對應型別的流,java8為這些流設定了一些特殊的操作,比如我們希望計算所有專業為電腦科學學生的年齡之和,那麼我們可以實現如下:

    Integer stu = students.stream()
            .filter(s->s.getMajor().equals("電腦科學"))
            .mapToInt(Student::getAge)
            .sum();

    System.out.println(stu);
2.2.2 flatMap
flatMap與map的區別在於 flatMap是將一個流中的每個值都轉成一個個流,然後再將這些流扁平化成為一個流 。舉例說明,假設我們有一個字串陣列String[] strs = {"java", "C#", "C++", "php", "javascrip","java"};,我們希望輸出構成這一陣列的所有非重複字元,那麼我們可能首先會想到如下實現:

        String[] strs = {"java", "C#", "C++", "php", "javascrip","java"};


        List<String> collect = Stream.of(strs)
                .map(s -> s.split(""))
                .distinct()
                .flatMap(Arrays::stream)
                .collect(Collectors.toList());

        collect.forEach(s-> System.out.println(s+": "));


三. 終端操作

3.1.1allMatch
allMatch用於檢測是否全部都滿足指定的引數行為,如果全部滿足則返回true,例如我們希望檢測是否所有的學生都已滿18週歲,那麼可以實現為:
    boolean isAdult = students.stream().allMatch(student -> student.getAge() >= 22);


    System.out.println(isAdult);
3.1.2anyMatch
anyMatch則是檢測是否存在一個或多個滿足指定的引數行為,如果滿足則返回true,例如我們希望檢測是否有來自武漢大學的學生,那麼可以實現為:

        boolean isAdult = students.stream().anyMatch(student -> student.getAge() >= 22);


        System.out.println(isAdult);



3.1.3 noneMathch
noneMatch用於檢測是否不存在滿足指定行為的元素,如果不存在則返回true,例如我們希望檢測是否不存在專業為電腦科學的學生,可以實現如下:

boolean noneCs = students.stream().noneMatch(student -> “電腦科學”.equals(student.getMajor()));


3.2 歸約

前面的例子中我們大部分都是通過collect(Collectors.toList())對資料封裝返回,如我的目標不是返回一個新的集合,而是希望對經過引數化操作後的集合進行進一步的運算,那麼我們可用對集合實施歸約操作。java8的流式處理提供了reduce方法來達到這一目的。

前面我們通過mapToInt將Stream<Student>對映成為IntStream,並通過IntStream的sum方法求得所有學生的年齡之和,實際上我們通過歸約操作,也可以達到這一目的,實現如下:
    Integer reduce = students.stream().map(Student::getAge).reduce(0, (a, b) -> a + b);

    System.out.println(reduce);

“`
持續精進………………………………….