摘要:前面的話中的于版本析出,平時項目中也有用到,今天就系統的來實踐一下。可以根據隊員的年齡進行分組執行結果為費爾南多費爾南迪尼奧隋維杰克魯伊夫卡爾德克阿德里安結果是一個,為不重復的年齡,為屬于該年齡的隊員列表。已經實現了分組。
【前面的話】Java中的Stream于1.8版本析出,平時項目中也有用到,今天就系統的來實踐一下。下面借用重慶力帆隊伍中我個人比較喜歡的球員來操作一波,隊員的年齡為了便于展示某些api做了調整,請不要太認真哦。
壹. Stream理解在java中我們稱Stream為『流』,我們經常會用流去對集合進行一些流水線的操作。stream就像工廠一樣,只需要把集合、命令還有一些參數灌輸到流水線中去,就可以加工成得出想要的結果。這樣的流水線能大大簡潔代碼,減少操作。給我個人的感覺類似JavaScript中的鏈式函數。
貳. Stream流程原集合 —> 流 —> 各種操作(過濾、分組、統計) —> 終端操作
Stream流的操作流程一般都是這樣的,先將集合轉為流,然后經過各種操作,比如過濾、篩選、分組、計算。最后的終端操作,就是轉化成我們想要的數據,這個數據的形式一般還是集合,有時也會按照需求輸出count計數。下文會一一舉例。
叁. API實踐首先,定義一個用戶對象,包含姓名、年齡、id三個成員變量:
package com.eelve.training.entity; import lombok.*; import javax.persistence.*; /** * @ClassName User * @Description TDO * @Author zhao.zhilue * @Date 2019/6/28 15:21 * @Version 1.0 **/ @Data @Entity @Table(name = "user") @ToString @NoArgsConstructor @AllArgsConstructor @EqualsAndHashCode(exclude={"id","name"}) public class User implements Comparable{ @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id") private Integer id; /** * Link name. */ @Column(name = "name", columnDefinition = "varchar(255) not null") private String name; @Column(name = "age") private Integer age; public User(String name, Integer age) { this.name = name; this.age = age; } @Override public int compareTo(User o) { return age.compareTo(o.getAge()); } }
然后在數據庫中插入測試數據,見下圖:
假如我們要實現過濾出40歲以下的隊員,我們可以這樣來實現:
@Test public void testUserStreamFilter(){ ListuserList = userMapper.getALL(); List resultList = userList.stream().filter(user -> user.getAge() <= 40).collect(Collectors.toList()); for (User user : resultList){ System.out.println(user.toString()); } }
filter里面,->箭頭后面跟著的是一個boolean值,可以寫任何的過濾條件,就相當于sql中where后面的東西,換句話說,能用sql實現的功能這里都可以實現
執行結果為:
User(id=1, name=費爾南多, age=25) User(id=2, name=費爾南迪尼奧, age=26) User(id=3, name=卡爾德克, age=27) User(id=4, name=阿德里安, age=28) User(id=5, name=隋維杰, age=26)2)distinct 去重
其用法和sql中的使用類似,假如我們要實現過去除用重復年齡的隊員,我們可以這樣來實現:
@Test public void testUserDistinct(){ ListuserList = userMapper.getALL(); List resultList = userList.stream().distinct().collect(Collectors.toList()); for (User user : resultList){ System.out.println(user.toString()); } }
執行結果為:
User(id=1, name=費爾南多, age=25) User(id=2, name=費爾南迪尼奧, age=26) User(id=3, name=卡爾德克, age=27) User(id=4, name=阿德里安, age=28) User(id=6, name=克魯伊夫, age=43)3)sorted排序
如果流中的元素的類實現了 Comparable 接口,即有自己的排序規則,那么可以直接調用 sorted() 方法對元素進行排序,如:
@Override public int compareTo(User o) { return age.compareTo(o.getAge()); }
@Test public void testUserStreamSorted(){ ListuserList = userMapper.getALL(); List resultList = userList.stream().sorted().collect(Collectors.toList()); for (User user : resultList){ System.out.println(user.toString()); } }
反之, 需要調用 sorted((T, T) -> int) 實現 Comparator 接口。
@Test public void testUserStreamSortedWithComparator(){ ListuserList = userMapper.getALL(); List resultList = userList.stream().sorted(Comparator.comparingInt(User::getAge)).collect(Collectors.toList()); for (User user : resultList){ System.out.println(user.toString()); } }
執行結果為:
User(id=1, name=費爾南多, age=25) User(id=2, name=費爾南迪尼奧, age=26) User(id=5, name=隋維杰, age=26) User(id=3, name=卡爾德克, age=27) User(id=4, name=阿德里安, age=28) User(id=6, name=克魯伊夫, age=43)4)limit() 返回前n個元素
如果想知道隊伍中年齡最小的就可以使用下面來實現:
@Test public void testUserStreamLimit(){ ListuserList = userMapper.getALL(); List resultList = userList.stream().limit(2).collect(Collectors.toList()); for (User user : resultList){ System.out.println(user.toString()); } }
執行結果為:
User(id=1, name=費爾南多, age=25) User(id=2, name=費爾南迪尼奧, age=26)5)skip
它的用法和limit正好相反,是去除前面幾個元素。
假如我們要去除前面兩個元素就可以使用下面的方法來實現:
@Test public void testUserStreamSkip(){ ListuserList = userMapper.getALL(); List resultList = userList.stream().skip(2).collect(Collectors.toList()); for (User user : resultList){ System.out.println(user.toString()); } }
執行結果為:
User(id=3, name=卡爾德克, age=27) User(id=4, name=阿德里安, age=28) User(id=5, name=隋維杰, age=26) User(id=6, name=克魯伊夫, age=43)6)組合使用
以上的過濾函數物品們可以組合來使用來實現我們具體的需求,示例代碼如下:
@Test public void testUserStreamSortLimit(){ ListuserList = userMapper.getALL(); List resultList = userList.stream().sorted().limit(5).collect(Collectors.toList()); for (User user : resultList){ System.out.println(user.toString()); } }
這樣我們就可以得到先排序后限制的結果:
User(id=1, name=費爾南多, age=25) User(id=2, name=費爾南迪尼奧, age=26) User(id=5, name=隋維杰, age=26) User(id=3, name=卡爾德克, age=27) User(id=4, name=阿德里安, age=28)3.2 映射 1)map(T->R)
map是將T類型的數據轉為R類型的數據,比如我們想要設置一個新的list,存儲用戶所有的城市信息。
@Test public void testUserStreamMap(){ ListuserList = userMapper.getALL(); List resultList = userList.stream().map(User::getAge).distinct().collect(Collectors.toList()); System.out.println(resultList.toString()); }
這樣我們可以得到所有年齡的樣本,執行結果為:
[25, 26, 27, 28, 43]2)flatMap(T -> Stream
將流中的每一個元素 T 映射為一個流,再把每一個流連接成為一個流。
@Test public void testStreamMap(){ ListhabitsList = new ArrayList<>(); habitsList.add("唱歌,聽歌"); habitsList.add("羽毛球,足球,登山"); habitsList = habitsList.stream().map(s -> s.split(",")).flatMap(Arrays::stream).collect(Collectors.toList()); System.out.println(habitsList); }
執行結果為:
[唱歌, 聽歌, 羽毛球, 足球, 登山]
這里原集合中的數據由逗號分割,使用split進行拆分后,得到的是Stream
檢測是否全部滿足參數行為,假如我們要檢測是不是所有隊員都是U21的球員:
@Test public void testUserStreamAllMatch(){ ListuserList = userMapper.getALL(); boolean isNotU21 = userList.stream().allMatch(user -> user.getAge() >= 21); System.out.println("是否都不是U21球員:" + isNotU21); }
執行結果為:
是否都不是U21球員:true2)anyMatch(T->boolean)
檢測是否有任意元素滿足給定的條件,比如,想知道是否有26歲的球員:
@Test public void testUserStreamAnyMatch(){ ListuserList = userMapper.getALL(); boolean isAgeU26 = userList.stream().anyMatch(user -> user.getAge() == 26); System.out.println("是否有26歲的球員:" + isAgeU26); }
執行結果為:
是否有26歲的球員:true3)noneMatch(T -> boolean)
流中是否有元素匹配給定的 T -> boolean 條件。比如我們要檢測是否含有U18的隊員:
@Test public void testUserStreamNoneMatch(){ ListuserList = userMapper.getALL(); boolean isNotU18 = userList.stream().noneMatch(user -> user.getAge() <= 18); System.out.println("是否都不是U18球員:" + isNotU18); }
執行結果為:
是否都不是U18球員:true
說明沒有U18的隊員。
4)findFirst( ):找到第一個元素@Test public void testUserFindFirst(){ ListuserList = userMapper.getALL(); Optional firstUser = userList.stream().sorted().findFirst(); System.out.println(firstUser.toString()); }
執行結果為:
Optional[User(id=1, name=費爾南多, age=25)]5)findAny():找到任意一個元素
@Test public void testUserFindAny(){ ListuserList = userMapper.getALL(); Optional anytUser = userList.parallelStream().sorted().findAny(); System.out.println(anytUser.toString()); }
執行結果為:
Optional[User(id=2, name=費爾南迪尼奧, age=26)]3.4 歸納計算 1)求隊員的總人數
@Test public void testUserCount(){ ListuserList = userMapper.getALL(); long totalAge = userList.stream().collect(Collectors.counting()); System.out.println("隊員人數為:" + totalAge); }
執行結果為:
隊員人數為:62)得到某一屬性的最大最小值
@Test public void testUserMaxAndMin(){ ListuserList = userMapper.getALL(); Optional userMaxAge = userList.stream().collect(Collectors.maxBy(Comparator.comparing(User::getAge))); System.out.println("年齡最大的隊員為:" + userMaxAge.toString()); Optional userMinAge = userList.stream().collect(Collectors.minBy(Comparator.comparing(User::getAge))); System.out.println("年齡最小的隊員為:" + userMinAge.toString()); }
執行結果為:
年齡最大的隊員為:Optional[User(id=6, name=克魯伊夫, age=43)] 年齡最小的隊員為:Optional[User(id=1, name=費爾南多, age=25)]3)求年齡總和是多少
@Test public void testUserSummingInt(){ ListuserList = userMapper.getALL(); int totalAge = userList.stream().collect(Collectors.summingInt(User::getAge)); System.out.println("年齡總和為:" + totalAge); }
執行結果為:
年齡總和為:175
我們經常會用BigDecimal來記錄金錢,假設想得到BigDecimal的總和:
// 獲得列表對象金額, 使用reduce聚合函數,實現累加器
BigDecimal sum = myList.stream() .map(User::getMoney)
.reduce(BigDecimal.ZERO,BigDecimal::add);
@Test public void testUserAveragingInt(){ ListuserList = userMapper.getALL(); Double totalAge = userList.stream().collect(Collectors.averagingInt(User::getAge)); System.out.println("平均年齡為:" + totalAge); }
執行結果為:
平均年齡為:29.1666666666666685)一次性得到元素的個數、總和、最大值、最小值
@Test public void testUserSummarizingInt(){ ListuserList = userMapper.getALL(); IntSummaryStatistics statistics = userList.stream().collect(Collectors.summarizingInt(User::getAge)); System.out.println("年齡的統計結果為:" + statistics ); }
執行結果為:
年齡的統計結果為:IntSummaryStatistics{count=6, sum=175, min=25, average=29.166667, max=43}6)字符串拼接
要將隊員的姓名連成一個字符串并用逗號分割。
@Test public void testUserJoining(){ ListuserList = userMapper.getALL(); String name = userList.stream().map(User::getName).collect(Collectors.joining(",")); System.out.println("所有的隊員名字:" + name ); }
執行結果為:
所有的隊員名字:費爾南多,費爾南迪尼奧,卡爾德克,阿德里安,隋維杰,克魯伊夫3.5 分組
在數據庫操作中,我們經常通過GROUP BY關鍵字對查詢到的數據進行分組,java8的流式處理也提供了分組的功能。使用Collectors.groupingBy來進行分組。
1)可以根據隊員的年齡進行分組@Test public void testUserGroupingBy(){ ListuserList = userMapper.getALL(); Map > ageMap = userList.stream().collect(Collectors.groupingBy(User::getAge)); for (Map.Entry > entry :ageMap.entrySet()){ System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue()); } }
執行結果為:
key= 25 and value= [User(id=1, name=費爾南多, age=25)] key= 26 and value= [User(id=2, name=費爾南迪尼奧, age=26), User(id=5, name=隋維杰, age=26)] key= 43 and value= [User(id=6, name=克魯伊夫, age=43)] key= 27 and value= [User(id=3, name=卡爾德克, age=27)] key= 28 and value= [User(id=4, name=阿德里安, age=28)]
結果是一個map,key為不重復的年齡,value為屬于該年齡的隊員列表。已經實現了分組。另外我們還可以繼續分組得到兩次分組的結果。
2)如果僅僅想統計各年齡的隊員個數是多少,并不需要對應的list按年齡分組并統計人數:
@Test public void testUserGroupingByCount(){ ListuserList = userMapper.getALL(); Map ageMap = userList.stream().collect(Collectors.groupingBy(User::getAge,Collectors.counting())); for (Map.Entry entry :ageMap.entrySet()){ System.out.println("隊員中" + entry.getKey() + "歲的隊員人數為:" + entry.getValue()); } }
執行結果為:
隊員中25歲的隊員人數為:1 隊員中26歲的隊員人數為:2 隊員中43歲的隊員人數為:1 隊員中27歲的隊員人數為:1 隊員中28歲的隊員人數為:13)partitioningBy 分區
分區與分組的區別在于,分區是按照 true 和 false 來分的,因此partitioningBy 接受的參數的 lambda 也是 T -> boolean
@Test public void testUserPartitioningBy (){ ListuserList = userMapper.getALL(); Map > partitioningByMap = userList.stream().collect(partitioningBy(user -> user.getAge() >= 30)); for (Map.Entry > entry :partitioningByMap.entrySet()){ System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue()); } }
執行結果為:
key= false and value= [User(id=1, name=費爾南多, age=25), User(id=2, name=費爾南迪尼奧, age=26), User(id=3, name=卡爾德克, age=27), User(id=4, name=阿德里安, age=28), User(id=5, name=隋維杰, age=26)] key= true and value= [User(id=6, name=克魯伊夫, age=43)]
【寫在后面的話】留下stream的類實現的方法和依賴圖,前面的實踐也只是挑選了幾個比較常用的Api。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/76086.html
摘要:本文主要介紹了中的閉包與局部套用功能,由國內管理平臺編譯呈現。譬如,認為給帶來了閉包特性就是其中之一。但是首先,我們將考慮如何利用閉包進行實現。很顯然,閉包打破了這一準則。這就是局部調用,它總是比閉包更為穩妥。 【編者按】本文作者為專注于自然語言處理多年的 Pierre-Yves Saumont,Pierre-Yves 著有30多本主講 Java 軟件開發的書籍,自2008開始供職于 ...
摘要:原文鏈接編程方法論響應式與代碼設計實戰序,來自于微信公眾號次靈均閣正文內容在一月的架構和設計趨勢報告中,響應式編程和函數式仍舊編列在第一季度的早期采納者中。 原文鏈接:《Java編程方法論:響應式RxJava與代碼設計實戰》序,來自于微信公眾號:次靈均閣 正文內容 在《2019 一月的InfoQ 架構和設計趨勢報告》1中,響應式編程(Reactive Programming)和函數式...
摘要:近日,在上列舉了開發中常見的個錯誤,與君共免。在多線程中并發修改集合內容是非常常見的,因此需要使用并發編程中常用的方法進行處理,例如同步鎖對于并發修改采用特殊的集合等等。在單線程和多線程情況下解決這個問題有微小的差別。 在編程時,開發者經常會遭遇各式各樣莫名錯誤。近日,Sushil Das 在 Geek On Java上列舉了 Java 開發中常見的 5 個錯誤,與君共「免」。 原文...
摘要:新特性總覽標簽本文主要介紹的新特性,包括表達式方法引用流默認方法組合式異步編程新的時間,等等各個方面。還有對應的和類型的函數連接字符串廣義的歸約匯總起始值,映射方法,二元結合二元結合。使用并行流時要注意避免共享可變狀態。 Java8新特性總覽 標簽: java [TOC] 本文主要介紹 Java 8 的新特性,包括 Lambda 表達式、方法引用、流(Stream API)、默認方...
摘要:根據對社區和新特性的深刻理解,他創作了函數式編程一書。問你在倫敦社區的經歷是否幫助你創作了函數式編程這本書絕對是這樣。我認為引入函數式編程會為很多編程任務提供方便。問之前的是面向對象的,現在全面支持函數式編程。 非商業轉載請注明作譯者、出處,并保留本文的原始鏈接:http://www.ituring.com.cn/article/199271 Richard Warburto...
閱讀 3696·2021-08-10 09:42
閱讀 591·2019-08-30 15:55
閱讀 890·2019-08-30 15:54
閱讀 3114·2019-08-30 13:45
閱讀 556·2019-08-29 16:23
閱讀 1992·2019-08-29 16:23
閱讀 986·2019-08-29 15:18
閱讀 2264·2019-08-29 12:57