Collection stream使用示例詳解
近日頻繁應(yīng)用 Stream 的 Api,記錄一下應(yīng)用實(shí)例。
基礎(chǔ)數(shù)據(jù)
實(shí)體類:
@Data
@Accessors(chain = true)
public static class Person {
private String name;
private Integer age;
private String hobby;
private LocalDateTime birthday;
}
數(shù)據(jù):
public List<Person> data() {
List<Person> data = new ArrayList<>();
data.add(new Person().setName("張三").setAge(25).setHobby("LOL,Food").setBirthday(LocalDateTime.of(1997, 1, 1, 0, 0)));
data.add(new Person().setName("李四").setAge(25).setHobby("CS:GO,Swimming").setBirthday(LocalDateTime.of(1997, 2, 1, 0, 0)));
data.add(new Person().setName("王五").setAge(30).setHobby("RedAlert2,Beer").setBirthday(LocalDateTime.of(1992, 3, 1, 0, 0)));
data.add(new Person().setName("趙六").setAge(40).setHobby("War3,Journey").setBirthday(LocalDateTime.of(1982, 4, 1, 0, 0)));
data.add(new Person().setName("孫七").setAge(40).setHobby("DOTA,Jogging").setBirthday(LocalDateTime.of(1982, 5, 1, 0, 0)));
return data;
}
元素轉(zhuǎn)Stream
當(dāng)我們需要將一個(gè)單值元素轉(zhuǎn)轉(zhuǎn)為一個(gè)多值元素,并進(jìn)行統(tǒng)一收集,flatMap在適合不過。
flatMap 函數(shù):將單個(gè)元素映射為Stream。
參數(shù):
Function mapper:元素映射為 Stream 的過程。
栗子:
/**
* 獲取所有成員的愛好集合
*/
@Test
public void test1() {
// 方式1:
// 先進(jìn)行 map 映射單個(gè)元素為多值元素
// 在將多值元素映射為 Stream
Set<String> hobbySet = data()
.stream()
.map(p -> p.getHobby().split(","))
.flatMap(Stream::of)
.collect(Collectors.toSet());
System.out.println(hobbySet);
// 方式2:直接將單個(gè)元素映射為 Stream
hobbySet = data()
.stream()
.flatMap(p -> Stream.of(p.getHobby().split(",")))
.collect(Collectors.toSet());
System.out.println(hobbySet);
}
輸出:
[War3, CS:GO, LOL, DOTA, Swimming, RedAlert2, Journey, Food, Beer, Jogging]
[War3, CS:GO, LOL, DOTA, Swimming, RedAlert2, Journey, Food, Beer, Jogging]
Terminal opt-Collectors.mapping
mapping 函數(shù):在聚合元素時(shí),對元素進(jìn)行映射轉(zhuǎn)換。若處理元素的中間操作階段進(jìn)行了map,那么此時(shí)屬于二次map。
參數(shù):
Function mapper:元素的映射過程。Collector downstream:對于映射后的元素的采集過程。
栗子:
Stream.of("1", "2", "3").map(Integer::parseInt).collect(Collectors.toList());
Stream.of("1", "2", "3").collect(Collectors.mapping(Integer::parseInt, Collectors.toList()));
這兩行代碼效果是一樣的,不過更推薦前者。
那么既然可以通過map實(shí)現(xiàn)同樣效果,為什么不直接使用map的方式呢?因?yàn)樵趯?shí)際應(yīng)用中,編寫一些復(fù)雜處理:處理分組后的下游數(shù)據(jù),Collectors.mapping更適用。
栗子:
/**
* 根據(jù)年齡分組,并統(tǒng)計(jì)每組下的成員姓名
*/
@Test
public void test2() {
Map<Integer, Set<String>> ageNameMapping = data()
.stream()
.collect(
Collectors.groupingBy(
Person::getAge,
Collectors.mapping(Person::getName, Collectors.toSet())
)
);
System.out.println(ageNameMapping);
}
輸出:
{40=[孫七, 趙六], 25=[李四, 張三], 30=[王五]}
Terminal opt-Collectors.toCollection&collectingAndThen
開發(fā)時(shí),經(jīng)常會(huì)遇到根據(jù)指定字段進(jìn)行分組的情況。有時(shí)需要對分組時(shí)產(chǎn)生的下游數(shù)據(jù)進(jìn)行其他操作,個(gè)人最經(jīng)常操作的就是排序。
toCollection函數(shù): 對聚合元素使用的容器進(jìn)行定制化。
參數(shù):
Supplier collectionFactory:定義裝載元素的容器。
collectingAndThen函數(shù): 聚合元素完畢后,對返回容器進(jìn)行二次處理。
參數(shù):
Collector downstream:聚合元素的過程。Function finisher:對downstream參數(shù)返回的容器的二次處理過程。處理后,需要將容器返回。
栗子:
/**
* 根據(jù)年齡分組,每組根據(jù)生日進(jìn)行排序
*/
@Test
public void test3() {
// 通過定制特定數(shù)據(jù)結(jié)構(gòu)的容器實(shí)現(xiàn)排序:正序
Map<Integer, Collection<Person>> ageMapping = data()
.stream()
.collect(
Collectors.groupingBy(
Person::getAge,
Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(Person::getBirthday)))
)
);
printMap(ageMapping);
// 通過定制特定數(shù)據(jù)結(jié)構(gòu)的容器實(shí)現(xiàn)排序:逆序
ageMapping = data()
.stream()
.collect(
Collectors.groupingBy(
Person::getAge,
Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(Person::getBirthday).reversed()))
)
);
printMap(ageMapping);
// 通過對聚合元素后返回的容器二次處理,實(shí)現(xiàn)排序
ageMapping = data()
.stream()
.collect(
Collectors.groupingBy(
Person::getAge,
Collectors.collectingAndThen(
Collectors.toList(),
l -> {
l.sort(Comparator.comparing(Person::getBirthday).reversed());
return l;
}
)
)
);
printMap(ageMapping);
}
public void printMap(Map<Integer, Collection<Person>> mapping) {
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
mapping.forEach((key, val) -> System.out.println(key + " : " + val.stream().map(p -> p.getName() + " -> " + dateTimeFormatter.format(p.getBirthday())).collect(Collectors.joining(" / "))));
System.out.println();
}輸出:
40 : 趙六 -> 1982-04-01 00:00:00 / 孫七 -> 1982-05-01 00:00:00
25 : 張三 -> 1997-01-01 00:00:00 / 李四 -> 1997-02-01 00:00:00
30 : 王五 -> 1992-03-01 00:00:0040 : 孫七 -> 1982-05-01 00:00:00 / 趙六 -> 1982-04-01 00:00:00
25 : 李四 -> 1997-02-01 00:00:00 / 張三 -> 1997-01-01 00:00:00
30 : 王五 -> 1992-03-01 00:00:0040 : 孫七 -> 1982-05-01 00:00:00 / 趙六 -> 1982-04-01 00:00:00
25 : 李四 -> 1997-02-01 00:00:00 / 張三 -> 1997-01-01 00:00:00
30 : 王五 -> 1992-03-01 00:00:00
Terminal opt-Collectors.toMap
有時(shí)我們分組后,可以確定每組的值是一個(gè)單值,而不是多值。這種情況下就可以使用toMap,避免取值時(shí)的不便。
toMap函數(shù):將聚合的元素組裝為一個(gè)Map返回。
參數(shù):
Function keyMapper:分組時(shí)使用的 KeyFunction valueMapper:將 Key 匹配元素的什么值作為 Key 的 ValueBinaryOperator mergeFunction:若發(fā)生單 Key 匹配到多個(gè)元素時(shí)的合并過程(只能返回一個(gè)元素作為 Key 的 Value)Supplier mapSupplier:指定裝載元素的 Map 類型容器
栗子:
/**
* 根據(jù)年齡分組,只保留生日最大的成員
*/
@Test
public void test4() {
// 下面注釋代碼會(huì)拋出異常,因?yàn)闆]有指定當(dāng)單Key匹配到多值時(shí)的 merge 行為
// 源碼中的默認(rèn)指定的 meger 行為則是拋出異常:throwingMerger()
// Map<Integer, Person> toMap = data().stream().collect(Collectors.toMap(Person::getAge, Function.identity()));
Map<Integer, Person> toMap = data()
.stream()
.collect(
Collectors.toMap(
Person::getAge,
Function.identity(),
(v1, v2) -> Comparator.comparing(Person::getBirthday).compare(v1, v2) > 0 ? v1 : v2
)
);
toMap.forEach((key, val) -> System.out.println(key + " : " + val.getName() + " -> " + DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(val.getBirthday())));
}輸出:
40 : 孫七 -> 1982-05-01 00:00:00
25 : 李四 -> 1997-02-01 00:00:00
30 : 王五 -> 1992-03-01 00:00:00
到此這篇關(guān)于Collection stream使用示例詳解的文章就介紹到這了,更多相關(guān)Collection stream內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
關(guān)于springboot 配置date字段返回時(shí)間戳的問題
這篇文章主要介紹了springboot 配置date字段返回時(shí)間戳的問題,在springboot2.0后,spring會(huì)將Date字段自動(dòng)給轉(zhuǎn)成UTC字符串了(在沒有配置的情況下),所以date需要轉(zhuǎn)換成時(shí)間戳還是yyyy-MM-dd HH:mm:ss,具體解決方法跟隨小編一起看看吧2021-07-07
springboot+vue制作后臺(tái)管理系統(tǒng)項(xiàng)目
本文詳細(xì)介紹了后臺(tái)管理使用springboot+vue制作,以分步驟、圖文的形式詳細(xì)講解,大家有需要的可以參考參考2021-08-08
Java實(shí)現(xiàn)從數(shù)據(jù)庫導(dǎo)出大量數(shù)據(jù)記錄并保存到文件的方法
這篇文章主要介紹了Java實(shí)現(xiàn)從數(shù)據(jù)庫導(dǎo)出大量數(shù)據(jù)記錄并保存到文件的方法,涉及Java針對數(shù)據(jù)庫的讀取及文件寫入等操作技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-11-11
SpringBoot集成JWT生成token及校驗(yàn)方法過程解析
這篇文章主要介紹了SpringBoot集成JWT生成token及校驗(yàn)方法過程解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-04-04
SpringBoot整合Mybatis-Plus實(shí)現(xiàn)微信注冊登錄的示例代碼
微信是不可或缺的通訊工具,本文主要介紹了SpringBoot整合Mybatis-Plus實(shí)現(xiàn)微信注冊登錄的示例代碼,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的可以了解一下2024-02-02
Java數(shù)據(jù)結(jié)構(gòu)之堆(優(yōu)先隊(duì)列)的實(shí)現(xiàn)
堆(優(yōu)先隊(duì)列)是一種典型的數(shù)據(jù)結(jié)構(gòu),其形狀是一棵完全二叉樹,一般用于求解topk問題。本文將利用Java語言實(shí)現(xiàn)堆,感興趣的可以學(xué)習(xí)一下2022-05-05

