Java集合Stream流操作的基本使用教程分享
Java 中可以使用 java.util.Stream 對(duì)一個(gè)集合(實(shí)現(xiàn)了java.util.Collection接口的類)做各種操作,例如:求和、過(guò)濾、排序等等。
這些操作可能是中間操作——返回一個(gè) Stream 流,或者是終端操作——返回一個(gè)結(jié)果。
流操作并不會(huì)影響原來(lái)的集合,可以簡(jiǎn)單認(rèn)為,流操作是把集合中的一個(gè)元素逐個(gè)復(fù)制放到一個(gè)首尾相接的流動(dòng)的水槽中。
Stream 流支持同步執(zhí)行,也支持并發(fā)執(zhí)行。如果我們直接獲取 stream 流,得到的是同步執(zhí)行的 stream 流;如果調(diào)用方法 parallelStream,則得到一個(gè)可以并發(fā)執(zhí)行的 Stream 流。
注意:Map不支持 Stream 流,但是它的 Key 和 Value 支持,因?yàn)樗鼈儗?shí)現(xiàn)了 Set 接口。
事前準(zhǔn)備
演示 Stream 流的提前準(zhǔn)備,創(chuàng)建幾個(gè)類以供測(cè)試
- 新建一個(gè)工具類,方便創(chuàng)建集合。
- 新建兩個(gè)類,例如開發(fā)中常見的數(shù)據(jù)庫(kù)實(shí)體類和 DTO 類。
public class MyUtil {
?
private static List<String> list = new ArrayList<>();
private static List<Student> students = new ArrayList<>();
?
static {
list.add("abc");
list.add("xyz");
list.add("fgh");
list.add("abc");
list.add("def");
list.add("xyz");
list.add("efg");
?
Student s1 = new Student();
s1.setAge("16");
s1.setId(UUID.randomUUID().toString());
s1.setName("張三");
s1.setMajor("計(jì)算機(jī)科學(xué)與技術(shù)");
Student s2 = new Student();
s2.setAge("18");
s2.setId(UUID.randomUUID().toString());
s2.setName("李四");
s2.setMajor("物聯(lián)網(wǎng)工程");
Student s3 = new Student();
s3.setAge("20");
s3.setId(UUID.randomUUID().toString());
s3.setName("王五");
s3.setMajor("網(wǎng)絡(luò)工程");
students.add(s1);
students.add(s2);
students.add(s3);
}
?
public static List<String> getList() {
return list;
}
public static List<Student> getStudents() {
return students;
}
}
?
public class Student {
?
private String id;
private String name;
private String age;
private String major;
}
?
public class StudentDTO {
?
private String name;
private String major;
}Filter
filter 可以幫助我們過(guò)濾流中的某些元素,其方法簽名如下
/* 過(guò)濾操作, Predicate 相當(dāng)于一個(gè)謂詞,即斷言流中的元素滿足某個(gè)條件,返回一個(gè) 布爾值 */ Stream<T> filter(Predicate<? super T> predicate);
具體使用方法如下:
public class Main {
?
public static void main(String[] args) {
List<String> list = MyUtil.getList();
System.out.println("過(guò)濾操作之前:");
System.out.println(list);
// 過(guò)濾不以 a 開頭的字符串,collect() 將流中的元素放到一個(gè)新的集合中
List<String> newList = list.stream().filter(s -> !s.startsWith("a")).collect(Collectors.toList());
System.out.println("-------------------------");
System.out.println("過(guò)濾操作之后:");
System.out.println(newList);
}
}
?
======== 輸出 =========
過(guò)濾操作之前:
[abc, xyz, fgh, abc, def, xyz, efg]
-------------------------
過(guò)濾操作之后:
[xyz, fgh, def, xyz, efg]
Sorted
sorted 可以幫助我們排序流中的元素,方法簽名如下:
/* 中間操作,傳入一個(gè) Comparator,對(duì)流中的元素進(jìn)行排序,如果不傳入,則使用默認(rèn)的 Comparable 排序 對(duì)原集合不影響 */ Stream<T> sorted(Comparator<? super T> comparator);
具體使用方法如下:
public class Main {
?
public static void main(String[] args) {
List<String> list = MyUtil.getList();
System.out.println("排序操作之前:");
System.out.println(list);
List<String> newList = list.stream().sorted().collect(Collectors.toList());
System.out.println("-------------------------");
System.out.println("排序操作之后:");
System.out.println(newList);
System.out.println("自定義排序:");
// 倒序排序。 forEach 方法可以用傳入的方法 逐個(gè) 處理流中的元素
list.stream().sorted((s1, s2)-> -s1.compareTo(s2)).forEach(System.out::println);
}
}
?
======== 輸出 =========
排序操作之前:
[abc, xyz, fgh, abc, def, xyz, efg]
-------------------------
排序操作之后:
[abc, abc, def, efg, fgh, xyz, xyz]
自定義排序:
xyz
xyz
fgh
efg
def
abc
abc
Map
Map 操作可以幫助我們將流中的一類元素映射為另一類元素,最典型的應(yīng)用就是可以用來(lái)將數(shù)據(jù)庫(kù)實(shí)體類轉(zhuǎn)換為供前端使用的 DTO 類。方法簽名如下:
/* 中間操作,可以將一個(gè)對(duì)象轉(zhuǎn)化為另一個(gè)對(duì)象 例如做 DTO 數(shù)據(jù)轉(zhuǎn)換 */ <R> Stream<R> map(Function<? super T, ? extends R> mapper);
具體使用方法如下:
public class Main {
?
public static void main(String[] args) {
List<Student> students = MyUtil.getStudents();
System.out.println("map 操作之前");
System.out.println(students);
// collect 方法可以將流中的元素收集到一個(gè) Collection 中,如果有去除重復(fù)元素的需求,可以考慮收集到 Set 中
List<StudentDTO> dtos = students.stream().map(student -> {
StudentDTO dto = new StudentDTO();
dto.setName(student.getName());
dto.setMajor(student.getMajor());
return dto;
}).collect(Collectors.toList());
System.out.println("-------------------------");
System.out.println("map 操作之后");
System.out.println(dtos);
}
}
?
======== 輸出 =========
map 操作之前
[Student{id='cb5726cd-e73a-443e-95e5-155aa6e876ae', name='張三', age='16', major='計(jì)算機(jī)科學(xué)與技術(shù)'}, Student{id='94478bae-b2ee-4c43-bac0-12f45f4099cd', name='李四', age='18', major='物聯(lián)網(wǎng)工程'}, Student{id='5fdd9e19-f7cf-4c61-b506-0ef58a36dcbe', name='王五', age='20', major='網(wǎng)絡(luò)工程'}]
-------------------------
map 操作之后
[StudentDTO{name='張三', major='計(jì)算機(jī)科學(xué)與技術(shù)'}, StudentDTO{name='李四', major='物聯(lián)網(wǎng)工程'}, StudentDTO{name='王五', major='網(wǎng)絡(luò)工程'}]
Match
/* 終端操作,可以用來(lái)匹配操作,返回一個(gè) boolean 值 可以方便地匹配集合中是否存在某種元素 */ // 只要集合中有一個(gè)匹配,就返回 true boolean anyMatch(Predicate<? super T> predicate); // 集合中所有元素都匹配,才返回 true boolean allMatch(Predicate<? super T> predicate); // 集合中所有元素都不匹配,返回 true boolean noneMatch(Predicate<? super T> predicate);
具體使用方法如下:
public class Main {
?
public static void main(String[] args) {
List<String> list = MyUtil.getList();
System.out.println("集合中的所有元素是否都以 a 開頭");
System.out.println(list.stream().allMatch(s -> s.startsWith("a")));
?
System.out.println("集合中是否存在元素以 a 開頭");
System.out.println(list.stream().anyMatch(s -> s.startsWith("a")));
?
System.out.println("集合中的元素是否都不以 a 開頭(相當(dāng)于 allMatch 的取反):");
System.out.println(list.stream().noneMatch(s -> s.startsWith("a")));
}
}
?
======== 輸出 =========
集合中的所有元素是否都以 a 開頭
false
集合中是否存在元素以 a 開頭
true
集合中的元素是否都不以 a 開頭(相當(dāng)于 allMatch 的取反):
falseCount
/* 終端操作,返回 stream 流中及集合中的元素個(gè)數(shù),返回一個(gè) long 類型 */ long count();
具體使用方法如下:
public class Main {
?
public static void main(String[] args) {
List<String> list = MyUtil.getList();
System.out.println(list);
System.out.println("集合中的個(gè)數(shù):" + list.size());
?
long count = list.stream().filter(s -> s.startsWith("a")).count();
System.out.println("集合中以 a 開頭的元素個(gè)數(shù):" + count);
}
}
?
======== 輸出 =========
[abc, xyz, fgh, abc, def, xyz, efg]
集合中的個(gè)數(shù):7
集合中以 a 開頭的元素個(gè)數(shù):2
Reduce
/* 終端操作,可以理解為減少集合的個(gè)數(shù),對(duì)集合中的元素不斷進(jìn)行累加,最終只得到一個(gè)元素 Optional 包含一個(gè)對(duì)象,可以防止空指針異常 */ Optional<T> reduce(BinaryOperator<T> accumulator);
具體使用方法如下:
public class Main {
?
public static void main(String[] args) {
List<String> list = MyUtil.getList();
// 可以理解為減少集合的個(gè)數(shù),對(duì)集合中的元素不斷進(jìn)行累加,最終只得到一個(gè)元素
// 例如對(duì)數(shù)字集合進(jìn)行累加進(jìn)行求和
String s = list.stream().reduce((s1, s2) -> s1 + "###" + s2).get();
System.out.println(s);
}
}
?
======== 輸出 =========
abc###xyz###fgh###abc###def###xyz###efg
總結(jié)
可以看到,stream 流操作并沒有什么使用難度,但如果不熟悉 Lambda 表達(dá)式開發(fā)和函數(shù)引用,則使用起來(lái)可能會(huì)稍微吃力些。
置于并發(fā)流的使用,只需要使用集合的方法parallelStream(),就可以獲得一個(gè)并發(fā)流,在編寫代碼上基本和同步流沒什么區(qū)別,因此學(xué)會(huì)上面的基本用法基本足夠了,實(shí)際使用過(guò)程中,根據(jù)實(shí)際情況決定如何使用即可。
以上就是Java集合Stream流操作的基本使用教程分享的詳細(xì)內(nèi)容,更多關(guān)于Java Stream流操作的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
java中你的項(xiàng)目應(yīng)該如何正確分層
這篇文章主要介紹了java中你的項(xiàng)目應(yīng)該如何正確分層,業(yè)務(wù)分層對(duì)于代碼規(guī)范是比較重要,決定著以后的代碼是否可復(fù)用,感興趣的可以了解一下2021-04-04
JAVA匿名內(nèi)部類(Anonymous Classes)的具體使用
本文主要介紹了JAVA匿名內(nèi)部類,匿名內(nèi)部類在我們JAVA程序員的日常工作中經(jīng)常要用到,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-08-08
Java中switch-case結(jié)構(gòu)的使用方法舉例詳解
這篇文章主要介紹了Java中switch-case結(jié)構(gòu)使用的相關(guān)資料,switch-case結(jié)構(gòu)是Java中處理多個(gè)分支條件的一種有效方式,它根據(jù)一個(gè)表達(dá)式的值來(lái)執(zhí)行不同的代碼塊,需要的朋友可以參考下2025-01-01
Elasticsearch?Recovery索引分片分配詳解
這篇文章主要為大家介紹了關(guān)于Elasticsearch的Recovery索引分片分配詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪<BR>2022-04-04
詳解SpringBoot 使用Spring Initializr 快速構(gòu)建工程(官方推薦)
本篇文章主要介紹了SpringBoot 使用Spring Initializr 快速構(gòu)建工程(官方推薦),非常具有實(shí)用價(jià)值,需要的朋友可以參考下2017-10-10
JSON--List集合轉(zhuǎn)換成JSON對(duì)象詳解
這篇文章主要介紹了List集合轉(zhuǎn)換成JSON對(duì)象,小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。2017-01-01

