天天看點

【java】入坑Stream,開發效率竄三竄

作者:老馬帶你學程式設計
【java】入坑Stream,開發效率竄三竄

在jdk1.8之前,我們對于集合的處理都是比較麻煩的,比如:做一次過濾,就會在代碼中使用for進行處理。jdk1.8後引入了Stream API來處理集合,使其變得簡單。

1、Stream簡介

Stream:流,可以對集合進行操作,比如:顧慮,合并等,是一種高效且易于使用處理資料的方法。

在Stream操作中分為三個步驟:

1、建立Stream流:比如:Stream.of()等

2、中間操作:即處理Stream流,比如:filter,map等

3、終止操作:即結束Stream流,傳回操作結果,比如:collect(Collectors.toList())

在操作Stream時,Stream自己不會存儲元素,不會改變原來對象,會延遲執行,隻會在需要結果時才執行。

2、Stream常用方法

準備工作,建立一個專家實體,便于進行方法操作。

/**
* @author: jiangjs
* @description:
* @date: 2022/7/15 15:37
**/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Expert {

    private String name;
    private String gender;
    private Integer age;
}
複制代碼           

建立一個集合:

List<Expert> experts = Arrays.asList(
                new Expert("jiashn","男",20),
                new Expert("張三-zhangsan","女",21),
                new Expert("王五-wangwu","女",26),
                new Expert("李四-lisi","男",24),
                new Expert("李四-lisi","男",24));
複制代碼           

2.1 建立Stream

stream()或parallelStream() :集合通過stream()可以建立順序流,parallelStream()建立并行流。

experts.stream()

experts.parallelStream()
複制代碼           

Stream.of() :将數組轉化成Stream流。

Stream<Expert> expertStream = Stream.of(new Expert("趙一-zhaoyi", "男", 20),
                new Expert("王定六-wangdingliu", "男", 21));
複制代碼           

2.2 Stream中間操作

2.2.1 filter

filter(Predicate p) :用于過濾滿足條件的資料,例如:上述專家中查找年齡小于等于25歲的人員。

//過濾年齡小于等于25的人
List<Expert> filterExperts = experts.stream().filter(expert -> expert.getAge() <= 25).collect(Collectors.toList());
System.out.println("年齡小于等于25歲的人的資訊:" + filterExperts);
複制代碼           

輸出:

年齡小于25歲的人的資訊:[Expert(name=jiashn, gender=男, age=20), Expert(name=張三-zhangsan, gender=女, age=21), Expert(name=李四-lisi, gender=男, age=24)]

2.2.2 map和flatMap

map(Function f) :将每個元素映射成新的元素,用于擷取集合中某個元素的資料資訊,例如:擷取所有專家的名字。

//擷取是以成員的名字資訊
String names = experts.stream().map(Expert::getName).collect(Collectors.joining(","));
System.out.println("所有人員的名稱:" + names);
複制代碼           

輸出:

所有人員的名稱:jiashn,張三-zhangsan,王五-wangwu,李四-lisi

flatMap(Function<? super T, ? extends Stream<? extends R>> mapper) :将流中每個值換成另一個流,并将所有流彙成一個流。例如:拆分名字,并擷取名字資訊。

//拆分名字
List<String> splitName = experts.stream().flatMap(exp -> Stream.of(exp.getName().split("-")))
        .collect(Collectors.toList());
System.out.println("拆分名字結果:" + splitName);
複制代碼           

輸出:

拆分名字結果:[jiashn, 張三, zhangsan, 王五, wangwu, 李四, lisi]

2.2.3 sorted

sorted() :排序,預設是升序排序,會産生一個新的流

sorted(Comparator<? super T> comparator) :排序,按照比較器順序排序,會産生一個新的流。例如:按照年齡進行排序

//年齡排序,正序或倒序
List<Expert> sortInfo = experts.stream().sorted(Comparator.comparing(Expert::getAge))
        .collect(Collectors.toList());
List<Expert> reversedInfo = experts.stream().sorted(Comparator.comparing(Expert::getAge).reversed())
        .collect(Collectors.toList());
System.out.println("人員年齡排序(升序):" + sortInfo+";人員年齡排序(降序):"+reversedInfo);
複制代碼           

輸出:

人員年齡排序(升序):[Expert(name=jiashn, gender=男, age=20), Expert(name=張三-zhangsan, gender=女, age=21), Expert(name=李四-lisi, gender=男, age=24), Expert(name=王五-wangwu, gender=女, age=26)];

人員年齡排序(降序):[Expert(name=王五-wangwu, gender=女, age=26), Expert(name=李四-lisi, gender=男, age=24), Expert(name=張三-zhangsan, gender=女, age=21), Expert(name=jiashn, gender=男, age=20)]

在比較器中使用 .reversed() 實作排序降序。

2.2.4 distinct()

distinct() :去重,通過流所生成的hashCode()和equals()去除重複元素。例如:去重上面集合中的李四.

//去重
List<Expert> disExperts = experts.stream().distinct()
        .collect(Collectors.toList());
System.out.println("去重:" + disExperts);
複制代碼           

輸出:

去重:[Expert(name=jiashn, gender=男, age=20), Expert(name=張三-zhangsan, gender=女, age=21), Expert(name=王五-wangwu, gender=女, age=26), Expert(name=李四-lisi, gender=男, age=24)]

2.2.5 limit、skip

limit(long maxSize) :截斷目前流,擷取不超過給定數量的元素。

skip(long n) :跳過元素,扔掉下标從0開始到n的元素的流,如果流中元素個數不超n個,則傳回一個空流,與limit()互補。例如:擷取下标從2開始的2個元素。

//截取記錄資訊
List<Expert> twoInfos = experts.stream().skip(2).limit(2).collect(Collectors.toList());
System.out.println("截取記錄:"+twoInfos);
複制代碼           

輸出:

截取記錄:[Expert(name=王五-wangwu, gender=女, age=26), Expert(name=李四-lisi, gender=男, age=24)]

我們也可以使用skip、limit的組合來對傳回回來的list資料進行分頁操作。

2.3 Stream終止操作

終端操作從流的流水線生成結果。

2.3.1 allMatch、anyMatch、noneMatch

allMatch(Predicate p) :檢查集合中所有元素是否與目前條件相比對。例如:驗證目前集合中元素性别是否都是男。

anyMatch(Predicate p) :檢查目前集合中是否存在元素與目前條件相比對。例如:驗證目前集合中元素是否存在性别男。

noneMatch(Predicate p) :檢查目前集合元素是否所有元素與目前條件不比對。例如:驗證目前集合所有元素性别都不為男。

//allMatch
boolean allMatch = experts.stream().allMatch(expert -> Objects.equals(expert.getGender(), "男"));
System.out.println("是否性别都為男:"+allMatch);
//anyMatch
boolean ageMatch = experts.stream().anyMatch(expert -> Objects.equals(expert.getGender(), "男"));
System.out.println("是否存在性别為男:"+ageMatch);
//noneMatch
boolean noneMatch = experts.stream().noneMatch(expert -> Objects.equals(expert.getGender(), "男"));
System.out.println("所有元素性别不為男:"+noneMatch);
複制代碼           

輸出:

是否性别都為男:false

是否存在性别為男:true

所有元素性别不為男:false

2.3.2 findFirst、findAny()

findFirst() :擷取集合中的第一個元素資訊,例如:擷取集合第一個元素資訊的姓名。

findAny() :擷取流中的任意元素,具有不确定性。

//findFirst
String name = experts.stream().findFirst().orElseGet(Expert::new).getName();
System.out.println("擷取第一個元素的姓名:"+name);
//findAny
String anyName = experts.stream().findAny().orElseGet(Expert::new).getName();
System.out.println("擷取符合條件任意元素的姓名:"+anyName);
複制代碼           

輸出:

擷取第一個元素的姓名:jiashn

擷取符合條件任意元素的姓名:jiashn

2.3.3 count

count() :統計流中元素個數,可以與filter進行使用來統計符合條件的元素個數。例如:統計年齡大于23歲的人員個數

//count
long count = experts.stream().filter(expert -> expert.getAge() > 23).count();
System.out.println("年齡大于23歲的人員個數:"+count);
複制代碼           

輸出:

年齡大于23歲的人員個數:3

2.3.4 max、min

max(Comparator c) :傳回流中符合條件的最大值。例如:擷取集合年齡最大的元素資訊。

min(Comparator c) :傳回流中符合條件的最小值。例如:擷取集合年齡最小的元素資訊。

//max
Expert maxExpert = experts.stream().max(Comparator.comparing(Expert::getAge)).get();
System.out.println("年齡最大人員資訊:"+maxExpert);
//min
Expert minExpert = experts.stream().min(Comparator.comparing(Expert::getAge)).get();
System.out.println("年齡最小人員資訊:"+minExpert);
複制代碼           

輸出:

年齡最大人員資訊:Expert(name=王五-wangwu, gender=女, age=26)

年齡最小人員資訊:Expert(name=jiashn, gender=男, age=20)

2.3.5 forEach

forEach(Consumer c) :内部循環,例如擷取所有元素的姓名

//forEach
StringJoiner sj = new StringJoiner(",");
experts.forEach(expert -> sj.add(expert.getName()));
System.out.println("所有人員姓名:"+sj.toString());
複制代碼           

輸出:

所有人員姓名:jiashn,張三-zhangsan,王五-wangwu,李四-lisi,李四-lisi

當然也可以使用map,Collectors.joining(",") 實作上述功能,參見map的使用。

2.3.6 reduce

reduce(T iden, BinaryOperator b) :将流中元素反複結合起來,得到一個值,傳回T

reduce(BinaryOperator b) :将流中元素反複結合起來,得到一個值,傳回Optional。例如:擷取所有人員年齡的總和

//reduce
int totalAge = experts.stream().mapToInt(Expert::getAge).reduce(0, (total, age) -> {
    total += age;
    return total;
});
System.out.println("所有人員年齡總和:"+totalAge);
複制代碼           

輸出:

所有人員年齡總和:115

2.3.7 collect(Collector c)

collect(Collector c) :将流轉換成其他形式。例如:list,set等。常用的方法:

toList:将流轉換成list集合輸出。上述例子中很多就用到

toSet:将流轉換成set集合輸出。例如:擷取姓名

joining:連接配接流中每個元素,傳回字元串,例如:運用map中的提到的擷取所有使用者姓名,使用","連接配接

groupingBy:分組,根據某些元素進行分組,例如:根據性别進行分組

partitioningBy:分區,根據true或false進行分區。例如:根據年齡大于23進行分區

summarizingDouble:彙總,統計元素的最大值,最小值等,例如:根據年齡進行彙總

當然stream還提供了其他的方法,如:counting,summingInt等。

//toSet
Set<String> nameSet = experts.stream().map(Expert::getName).collect(Collectors.toSet());
System.out.println("所有姓名:"+nameSet);

//groupingBy
Map<String, List<Expert>> genderMap = experts.stream().collect(Collectors.groupingBy(Expert::getGender));
System.out.println("性别分組資訊:"+genderMap);

//summarizingDouble
DoubleSummaryStatistics statistics = experts.stream().collect(Collectors.summarizingDouble(Expert::getAge));
System.out.println("年齡最大值:"+statistics.getMax());
System.out.println("年齡最小值:"+statistics.getMin());
System.out.println("年齡平均值:"+statistics.getAverage());
System.out.println("年齡總和值:"+statistics.getSum());
System.out.println("總人數:"+statistics.getCount());

//partitioningBy
Map<Boolean, List<Expert>> collect = experts.stream().collect(Collectors.partitioningBy(jia -> jia.getAge() > 23));
List<Expert> gtExpert= collect.get(Boolean.TRUE);
List<Expert> ltExpert= collect.get(Boolean.FALSE);
System.out.println("年齡大于23歲的資料:"+gtExpert);
System.out.println("年齡小于等于23歲的資料:"+ltExpert);
複制代碼           

輸出:

所有姓名:[jiashn, 張三-zhangsan, 王五-wangwu, 李四-lisi]

性别分組資訊:{女=[Expert(name=張三-zhangsan, gender=女, age=21), Expert(name=王五-wangwu, gender=女, age=26)], 男=[Expert(name=jiashn, gender=男, age=20), Expert(name=李四-lisi, gender=男, age=24), Expert(name=李四-lisi, gender=男, age=24)]}

年齡最大值:26.0

年齡最小值:20.0

年齡平均值:23.0

年齡總和值:115.0

總人數:5

年齡大于23歲的資料:[Expert(name=王五-wangwu, gender=女, age=26), Expert(name=李四-lisi, gender=男, age=24), Expert(name=李四-lisi, gender=男, age=24)]

年齡小于等于23歲的資料:[Expert(name=jiashn, gender=男, age=20), Expert(name=張三-zhangsan, gender=女, age=21)]

jdk1.8中的Stream的出現,讓我們對集合的操作變得簡單,業精于勤荒于嬉,我們多多使用,自然會對這些方法使用熟練掌握。