巧用Java8中的Stream,讓集合操作飛起來!

2020-12-12 酷扯兒

本文轉載自【微信公眾號:java進階架構師,ID:java_jiagoushi】經微信公眾號授權轉載,如需轉載與原文作者聯繫

作者:堅持就是勝利

juejin.im/post/5d5e2616f265da03b638b28a

簡介

java8也出來好久了,接口默認方法,lambda表達式,函數式接口,Date API等特性還是有必要去了解一下。比如在項目中經常用到集合,遍歷集合可以試下lambda表達式,經常還要對集合進行過濾和排序,Stream就派上用場了。用習慣了,不得不說真的很好用。

Stream作為java8的新特性,基於lambda表達式,是對集合對象功能的增強,它專注於對集合對象進行各種高效、便利的聚合操作或者大批量的數據操作,提高了編程效率和代碼可讀性。

Stream的原理:將要處理的元素看做一種流,流在管道中傳輸,並且可以在管道的節點上處理,包括過濾篩選、去重、排序、聚合等。元素流在管道中經過中間操作的處理,最後由最終操作得到前面處理的結果。

集合有兩種方式生成流:

stream() 為集合創建串行流parallelStream() - 為集合創建並行流

上圖中是Stream類的類結構圖,裡面包含了大部分的中間和終止操作。

中間操作主要有以下方法(此類型方法返回的都是Stream):map (mapToInt, flatMap 等)、 filter、 distinct、 sorted、 peek、 limit、 skip、 parallel、 sequential、 unordered

終止操作主要有以下方法:forEach、 forEachOrdered、 toArray、 reduce、 collect、 min、 max、 count、 anyMatch、 allMatch、 noneMatch、 findFirst、 findAny、 iterator

舉例說明

首先為了說明Stream對對象集合的操作,新建一個Student類(學生類),覆寫了equals()和hashCode()方法

public class Student {private Long id; private String name; private int age; private String address; public Student() {} public Student(Long id, String name, int age, String address) { this.id = id; this.name = name; this.age = age; this.address = address; } @Override public String toString() { return "Student{" + "id=" + id + ", name='" + name + '\'' + ", age=" + age + ", address='" + address + '\'' + '}'; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Student student = (Student) o; return age == student.age && Objects.equals(id, student.id) && Objects.equals(name, student.name) && Objects.equals(address, student.address); } @Override public int hashCode() { return Objects.hash(id, name, age, address); } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; }}

filter(篩選)

public static void main(String [] args) {Student s1 = new Student(1L, "肖戰", 15, "浙江"); Student s2 = new Student(2L, "王一博", 15, "湖北"); Student s3 = new Student(3L, "楊紫", 17, "北京"); Student s4 = new Student(4L, "李現", 17, "浙江"); List<Student> students = new ArrayList<>(); students.add(s1); students.add(s2); students.add(s3); students.add(s4); List<Student> streamStudents = testFilter(students); streamStudents.forEach(System.out::println); } /** * 集合的篩選 * @param students * @return */ private static List<Student> testFilter(List<Student> students) { //篩選年齡大於15歲的學生// return students.stream().filter(s -> s.getAge()>15).collect(Collectors.toList()); //篩選住在浙江省的學生 return students.stream().filter(s ->"浙江".equals(s.getAddress())).collect(Collectors.toList()); }

運行結果:

這裡我們創建了四個學生,經過filter的篩選,篩選出地址是浙江的學生集合。

map(轉換)

public static void main(String [] args) { Student s1 = new Student(1L, "肖戰", 15, "浙江"); Student s2 = new Student(2L, "王一博", 15, "湖北"); Student s3 = new Student(3L, "楊紫", 17, "北京"); Student s4 = new Student(4L, "李現", 17, "浙江"); List<Student> students = new ArrayList<>(); students.add(s1); students.add(s2); students.add(s3); students.add(s4); testMap(students); } /** * 集合轉換 * @param students * @return */ private static void testMap(List<Student> students) { //在地址前面加上部分信息,只獲取地址輸出 List<String> addresses = students.stream().map(s ->"住址:"+s.getAddress()).collect(Collectors.toList()); addresses.forEach(a ->System.out.println(a)); }

運行結果

map就是將對應的元素按照給定的方法進行轉換。

distinct(去重)

public static void main(String [] args) { testDistinct1(); } /** * 集合去重(基本類型) */ private static void testDistinct1() { //簡單字符串的去重 List<String> list = Arrays.asList("111","222","333","111","222"); list.stream().distinct().forEach(System.out::println); }

public static void main(String [] args) {testDistinct2(); } /** * 集合去重(引用對象) */ private static void testDistinct2() { //引用對象的去重,引用對象要實現hashCode和equal方法,否則去重無效 Student s1 = new Student(1L, "肖戰", 15, "浙江"); Student s2 = new Student(2L, "王一博", 15, "湖北"); Student s3 = new Student(3L, "楊紫", 17, "北京"); Student s4 = new Student(4L, "李現", 17, "浙江"); Student s5 = new Student(1L, "肖戰", 15, "浙江"); List<Student> students = new ArrayList<>(); students.add(s1); students.add(s2); students.add(s3); students.add(s4); students.add(s5); students.stream().distinct().forEach(System.out::println); }

可以看出,兩個重複的「肖戰」同學進行了去重,這不僅因為使用了distinct()方法,而且因為Student對象重寫了equals和hashCode()方法,否則去重是無效的。

sorted(排序)

public static void main(String [] args) { testSort1(); } /** * 集合排序(默認排序) */ private static void testSort1() { List<String> list = Arrays.asList("333","222","111"); list.stream().sorted().forEach(System.out::println); }

public static void main(String [] args) { testSort2(); } /** * 集合排序(指定排序規則) */ private static void testSort2() { Student s1 = new Student(1L, "肖戰", 15, "浙江"); Student s2 = new Student(2L, "王一博", 15, "湖北"); Student s3 = new Student(3L, "楊紫", 17, "北京"); Student s4 = new Student(4L, "李現", 17, "浙江"); List<Student> students = new ArrayList<>(); students.add(s1); students.add(s2); students.add(s3); students.add(s4); students.stream() .sorted((stu1,stu2) ->Long.compare(stu2.getId(), stu1.getId())) .sorted((stu1,stu2) -> Integer.compare(stu2.getAge(),stu1.getAge())) .forEach(System.out::println); }

上面指定排序規則,先按照學生的id進行降序排序,再按照年齡進行降序排序

limit(限制返回個數)

public static void main(String [] args) { testLimit(); } /** * 集合limit,返回前幾個元素 */ private static void testLimit() { List<String> list = Arrays.asList("333","222","111"); list.stream().limit(2).forEach(System.out::println); }

skip(刪除元素)

public static void main(String [] args) { testSkip(); } /** * 集合skip,刪除前n個元素 */ private static void testSkip() { List<String> list = Arrays.asList("333","222","111"); list.stream().skip(2).forEach(System.out::println); }

reduce(聚合)

public static void main(String [] args) { testReduce(); } /** * 集合reduce,將集合中每個元素聚合成一條數據 */ private static void testReduce() { List<String> list = Arrays.asList("歡","迎","你"); String appendStr = list.stream().reduce("北京",(a,b) -> a+b); System.out.println(appendStr); }

min(求最小值)

public static void main(String [] args) { testMin(); } /** * 求集合中元素的最小值 */ private static void testMin() { Student s1 = new Student(1L, "肖戰", 14, "浙江"); Student s2 = new Student(2L, "王一博", 15, "湖北"); Student s3 = new Student(3L, "楊紫", 17, "北京"); Student s4 = new Student(4L, "李現", 17, "浙江"); List<Student> students = new ArrayList<>(); students.add(s1); students.add(s2); students.add(s3); students.add(s4); Student minS = students.stream().min((stu1,stu2) ->Integer.compare(stu1.getAge(),stu2.getAge())).get(); System.out.println(minS.toString()); }

上面是求所有學生中年齡最小的一個,max同理,求最大值。

anyMatch/allMatch/noneMatch(匹配)

public static void main(String [] args) { testMatch(); } private static void testMatch() { Student s1 = new Student(1L, "肖戰", 15, "浙江"); Student s2 = new Student(2L, "王一博", 15, "湖北"); Student s3 = new Student(3L, "楊紫", 17, "北京"); Student s4 = new Student(4L, "李現", 17, "浙江"); List<Student> students = new ArrayList<>(); students.add(s1); students.add(s2); students.add(s3); students.add(s4); Boolean anyMatch = students.stream().anyMatch(s ->"湖北".equals(s.getAddress())); if (anyMatch) { System.out.println("有湖北人"); } Boolean allMatch = students.stream().allMatch(s -> s.getAge()>=15); if (allMatch) { System.out.println("所有學生都滿15周歲"); } Boolean noneMatch = students.stream().noneMatch(s -> "楊洋".equals(s.getName())); if (noneMatch) { System.out.println("沒有叫楊洋的同學"); } }

anyMatch:Stream 中任意一個元素符合傳入的 predicate,返回 true

allMatch:Stream 中全部元素符合傳入的 predicate,返回 true

noneMatch:Stream 中沒有一個元素符合傳入的 predicate,返回 true

總結

上面介紹了Stream常用的一些方法,雖然對集合的遍歷和操作可以用以前常規的方式,但是當業務邏輯複雜的時候,你會發現代碼量很多,可讀性很差,明明一行代碼解決的事情,你卻寫了好幾行。試試lambda表達式,試試Stream,你會有不一樣的體驗。

相關焦點

  • 一文帶你入門Java Stream流,太強了
    Stream 就好像一個高級的迭代器,但只能遍歷一次,就好像一江春水向東流;在流的過程中,對流中的元素執行一些操作,比如「過濾掉長度大於 10 的字符串」、「獲取每個字符串的首字母」等。要想操作流,首先需要有一個數據源,可以是數組或者集合。
  • JAVA8——JAVA成長之路
    在最簡單的形式中,一個lambda可以由用逗號分隔的參數列表、–>符號與函數體三部分表示。例如:請注意參數e的類型是由編譯器推測出來的。Stream API極大簡化了集合框架的處理(但它的處理的範圍不僅僅限於集合框架的處理,這點後面我們會看到)。
  • 終於搞懂了java8的內存結構,再也不糾結方法區和常量池了
    java8內存結構介紹  java虛擬機在jdk8改變了許多,網絡上各種解釋都有,在查閱了官方文檔以及一下大佬的解釋以後,我來粗淺的介紹一下我理解的java8的內存結構。java8內存結構圖
  • mysql進行查詢時可以用limit限制查詢結果的條數,Java可以嗎?
    java8流操作之map映射》、《給Java8的流加個篩子》)的朋友也許會說,該不會還是java8中流式編程中提供的功能吧。今天要說的就是通過對流的處理,實現類似sql中limit的效果。還是用一下之前的類和準備的數據。接下來我們來從這個集合list中取前兩條試試。具體要怎麼做呢,往下看吧。
  • 學起來:生活中巧用調味品的小妙招
    這裡,我們通過《秘制調味料》這本書,來學一學生活中巧用調味品的小妙招。鮮嫩的食材要儘量少用調味品新鮮、醇香的食材,要儘量少用調味品,比如應季的蔬菜、瓜果、新鮮的魚肉等等,這些食材,如果用過多調味品,尤其是辛香料之類,就會掩蓋食材本身自帶的鮮味,甚至會影響菜餚的口感。
  • 會寫Java,不一定會用Collections,你會用嗎
    1.Collectors 主要是用在java stream 中,是用來最後處理stream的,比如 Collectors.toSet()。2.Collection 是所有集合類的接口類,比如常用的Set,List,Map。
  • Java8中如何對流做類似sql的order by操作
    mysql進行查詢時可以用limit限制查詢結果的條數,Java可以嗎?》和大家約定好了,今天要寫一寫怎麼對流做類似order by的操作。既然說到,那就要做到。今天就來寫一寫Java8中怎麼對流做類似sql中order by的操作。俗話說:「一事不煩二主」,還是寫流相關的內容,那就還麻煩之前的類和數據登場演繹一番了。
  • 氣泡水機Sodastream登臺10週年推出LINE FRIENDS「限量熊大水瓶」
    氣泡水機品牌sodastream正式登臺10週年了!幸好有它的存在,不愛喝白開水的人終於有了健康的飲品選擇。在10年內sodastream總共引進超過9個系列,為臺灣省下15億支一次性塑膠瓶,足以環島30趟。
  • 巧用2個操作,就能瞬間找到!
    為了讓小夥伴們更好地了解遊戲中的內容,希望大家能動動小手關注一下。本期內容去往末地擊殺了末影龍後,在周圍可以找到折躍門可以進入外島。之所以要去往末地外島,是因為這裡有著末地城的存在。很多非酋玩家剛進入外島時,通過徒步很難找到末地城的蹤跡,這時候巧用2個操作,就能瞬間找到。
  • 初識Spring Cloud Stream,什麼是消息驅動微服務框架
    如上圖所示,有時候系統中的某個服務會因為用戶操作或內部行為發布一個事件,該服務知道這個事件在將來的某一個時間點會被其他服務所消費,但是並不知道這個服務具體是誰、也不關心什麼時候被消費。spring cloud stream是一個構建與Spring Boot和Spring Integration之上的框架,方便開發人員快速構建基於Message-Driven的系統。
  • 《動物派對》飛踢如何操作 飛踢動作操作技巧分享
    導 讀 動物派對手遊裡飛踢怎麼操作呢?
  • 巧用拼圖遊戲 讓寶寶快樂學英語
    新東方網>學前>幼兒教育>趣味學習樂園>學前班英語>少兒英語>正文巧用拼圖遊戲 讓寶寶快樂學英語 2013-02-01 11:07 來源:滬江英語
  • 《集合啦動物之森》存檔轉移功能操作答疑
    《集合啦動物之森》的全新存檔轉移功能已經在今天正式實裝了,官方也公布了具體存檔轉移功能的介紹、操作及注意事項。由於《集合啦動物之森》特別的一臺主機就是一個小島的設置,導致了《集合啦動物之森》無法使用switch系統原有的存檔轉移和雲存檔功能,因此為了解決這個問題,官方特別推出這個全新獨立的存檔轉移功能應用,以便玩家可以自己操作存檔轉移。本次存檔轉移提供了兩個功能,即全島轉移和玩家轉移。
  • 程式設計師:Java操作導出excel的三種方法,POI、easyExcel、Hutool
    前言最近在開發一個管理後臺,有一些excel的導出和導入操作,以前都是使用POI操作excel,這一次嘗試了別的人組件:easyExcel和Hutool,今天就來分享一下,這三種方式中POI操作導出excel。POI導出excel文件。
  • 俄羅斯最富盛名的巧克力愛蓮巧是如何生產的?
    愛蓮巧發明於1965年。當時莫斯科紅十月糖果試驗廠(Red October)的糖果點心師們領受了一項任務:研發一種新型巧克力、生產出某種既美味獨特又不貴的新型糖果。巧克力配方的研究工作持續了幾年。起初建議生產帶柑橘皮的巧克力,爾後一個專門委員會拍板生產今日可在商店中買到的牛奶味巧克力。
  • 《集合啦動物森友會》變身棒怎麼用 星星棒使用方法介紹_18183集合...
    動物森友會 > 正文 《集合啦動物森友會》變身棒怎麼用 星星棒使用方法介紹 2020-04-03 14:04:37 模擬經營SIM 人氣值:加載中...
  • 赤坎區:七夕古風巧韻「動起來」
    記者 林石湛 攝 為加強市民群眾文化交流,進一步助力全國文明城市創建,8月25日,赤坎區創文辦、團區委、區文旅體局、區文化館、沙灣街道辦等單位聯合舉辦了「我們的節日·七夕」——古風巧韻傳統文化活動,現場吸引了近500名市民群眾參加。