【java8】持續精進-之流式資料處理
阿新 • • 發佈:2019-02-19
流式處理簡介
在我接觸到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);
“`
持續精進………………………………….