Java 8 新特性之Stream API

1. 概述

1.1 簡介

Java 8 中有兩大最為重要的改革,第一個是 Lambda 表達式,另外一個則是 Stream API(java.util.stream.*)。

Stream 是 Java 8 中處理集合的關鍵抽象概念,它可以指定你希望對集合進行的操作,可以執行非常復雜的查找、過濾和映射數據等操作。使用 Stream API 對集合數據進行,就類似于使用 SQL 執行的數據庫查詢。也可以使用 Stream API 來并行執行操作。簡而言之,Stream API 提供了一種高效且易于使用的處理數據的方式。

1.2 流(Stream)到底是什么呢?

是數據渠道,用于操作數據源(集合、數組等)所生成的元素序列。“集合講的是數據,流講的是計算”

Stream(流)是一個來自數據源的元素隊列并支持聚合操作

  • 元素是特定類型的對象,形成一個隊列。 Java中的Stream并不會存儲元素,而是按需計算。
  • 數據源 流的來源。 可以是集合,數組,I/O channel, 產生器generator 等。
  • 聚合操作 類似SQL語句一樣的操作, 比如 filter, map, reduce, find, match, sorted 等。

和以前的 Collection 操作不同, Stream 操作還有兩個基礎的特征:

  • Pipelining: 中間操作都會返回流對象本身。 這樣多個操作可以串聯成一個管道, 如同流式風格(fluent style)。 這樣做可以對操作進行優化, 比如延遲執行(laziness)和短路( short-circuiting)。
  • 內部迭代: 以前對集合遍歷都是通過 Iterator 或者 For-Each 的方式, 顯式的在集合外部進行迭代, 這叫做外部迭代。 Stream提供了內部迭代的方式, 通過訪問者模式(Visitor)實現。

注意 :

  1. Stream 自己不會存儲元素
  2. Stream 不會改變源對象,相反,它們會返回一個持有結果的新 Stream
  3. Stream 操作時延遲執行的。這意味著它們會等到需要結果的時候才執行

1.3 Stream 操作的三個步驟

  1. 創建 Stream

    • 一個數據源(集合、數組等),獲取一個流
  2. 中間操作(聚合操作)

    • 一個中間操作鏈,對數據源的數據進行處理
  3. 終止操作(終端操作)

    • 一個終止操作,執行中間操作鏈,并產生結果

Java Stream

2. 創建 Stream(流)

在 Java 8 中, 集合接口有兩個方法來生成流:

  • stream() ? 為集合創建串行流。
  • parallelStream() ? 為集合創建并行流。

創建 Stream 的 5 種方式

@Test
public void t1() {// 1. Collection 提供了兩個方法  stream() 與 parallelStream()List<String> list = new ArrayList<>();Stream<String> stream = list.stream(); //獲取一個順序流Stream<String> parallelStream = list.parallelStream(); //獲取一個并行流// 2. 通過 Arrays 中的 stream() 獲取一個數組流Integer[] nums = new Integer[10];Stream<Integer> stream1 = Arrays.stream(nums);// 3. 通過 Stream 類中靜態方法 of()Stream<Integer> stream2 = Stream.of(1,2,3,4,5,6);// 4. 創建無限流 - 迭代Stream<Integer> stream3 = Stream.iterate(0, (x) -> x + 2).limit(20);stream3.forEach(System.out::println);// 5. 創建無限流 - 生成Stream<Double> stream4 = Stream.generate(Math::random).limit(5);stream4.forEach(System.out::println);
}

3. Stream 的中間操作

多個中間操作可以連接起來形成一個流水線,除非流水線上觸發終止操作,否則中間操作不會執行任何處理,而是在終止操作時一次性全部處理,稱為“惰性求值”

提供基礎的操作數據

List<Employee> emps = Arrays.asList(new Employee(1, "a1", 28, 3888.99),new Employee(2, "a2", 49, 336.66),new Employee(3, "a3", 18, 3323.33),new Employee(4, "a4", 38, 6666.77),new Employee(5, "a5", 8, 80.88),new Employee(5, "a5", 8, 80.88),new Employee(5, "a5", 8, 80.88),new Employee(6, "a6", 56, 100.66)
);

3.1 篩選與切片

  • filter 接收Lambda,從流中排除某些元素。
  • limit 截斷流,使元素不超過給定數量
  • skip(n) 跳過元素,返回一個扔掉了前 n 個元素的流,若流中元素不足 n 個,則返回一個空流,與 limit 互補
  • distinct 篩選去重,通過流所生成元素的 hashCode()equals() 去除重復元素

1. filter 接收Lambda,從流中排除某些元素

@Test
public void t2() {// 中間操作:不會執行任何操作Stream<Employee> stream = emps.stream().filter((e) -> {System.out.println("中間操作");return e.getAge() > 20;});// 終止操作:一次性執行全部內容,即"惰性求值"stream.forEach(System.out::println);}

2. limit 截斷流

@Test
public void t3() {emps.stream().filter((e) -> {// 當達到 limit 為 2 時將不繼續遍歷,稱為短路,以提高效率System.out.println("短路");return e.getSalary() > 3000;}).limit(2).forEach(System.out::println);
}

3. skip 跳過元素

@Test
public void t4() {emps.stream().filter(e -> e.getSalary() > 100).skip(2).forEach(System.out::println);
}

4. distinct 篩選

@Test
public void t5() {emps.stream().distinct().forEach(System.out::println);
}

要使用 distinct 需要重寫 EmployeehashCode()equals() 方法

@Override
public int hashCode() {final int prime = 31;int result = 1;result = prime * result + age;result = prime * result + id;result = prime * result + ((name == null) ? 0 : name.hashCode());long temp;temp = Double.doubleToLongBits(salary);result = prime * result + (int) (temp ^ (temp >>> 32));return result;
}@Override
public boolean equals(Object obj) {if (this == obj)return true;if (obj == null)return false;if (getClass() != obj.getClass())return false;Employee other = (Employee) obj;if (age != other.age)return false;if (id != other.id)return false;if (name == null) {if (other.name != null)return false;} else if (!name.equals(other.name))return false;if (Double.doubleToLongBits(salary) != Double.doubleToLongBits(other.salary))return false;return true;
}

3.2 映射

  • map 接收 Lambda,將元素轉換成其它形式或提取信息。接收一個函數作為參數,該函數會被應用到每個元素上,并將其映射成一個新的元素
  • flatMap 接收一個函數作為參數,將流中的每個值都換成另一個流,然后把所有流連接成一個流

1. map

將原有的元素進過函數處理,讓后映射(覆蓋)成一個新的元素

@Test
public void t6() {List<String> list = Arrays.asList("aa","bb","cc","dd");list.stream().map((s) -> s.toUpperCase()).forEach(System.out::println);System.out.println("------------------------------------");emps.stream().map(Employee::getName).forEach(System.out::println);
}

2. flatMap

基礎方法

/*** 將字符串分解成字符 list,并返回 Stream* * @param str 待分解字符串* @return Stream*/
public static Stream<Character> filterCharacter(String str) {List<Character> list = new ArrayList<>();for (Character ch : str.toCharArray()) {list.add(ch);}return list.stream();
}

正常情況下,當 filterCharacter 返回的也是一個 Stream 時,相當于流里面還有子流,接收的結果就是 Stream<Stream<Character>>,如果我們要進行遍歷的話,就需要使用兩層 forEach 才能遍歷完成。

@Test
public void t7() {List<String> list = Arrays.asList("aa","bb","cc","dd");Stream<Stream<Character>> stream = list.stream().map(StreamTest1::filterCharacter);// 因為 Stream 還是 Stream 所以需要嵌套 forEach 才能進行遍歷stream.forEach((sm) -> {sm.forEach(System.out::println);});
}

但如果使用 flatMap 就可以將每個子流都合并成一個流,這樣遍歷的時候只使用一層 forEach 就可以了

@Test
public void t8() {List<String> list = Arrays.asList("aa","bb","cc","dd");Stream<Character> stream = list.stream().flatMap(StreamTest1::filterCharacter);stream.forEach(System.out::println);
}

3.3 排序

  • sorted 自然排序(Comparable)
  • sorted(Comparator com) 定制排序(Comparator)

1. sorted 自然排序

@Test
public void t9() {List<String> list = Arrays.asList("cc","aa","dd","bb");list.stream().sorted().forEach(System.out::println);
}

2. sorted(Comparator com) 定制排序

@Test
public void t10() {emps.stream().sorted((e1,e2) -> Integer.compare(e1.getAge(),e2.getAge())).forEach(System.out::println);
}

4. Stream 終止操作

4.1 查找與匹配

  • allMatch 檢查是否匹配所有元素
  • anyMatch 檢查是否至少匹配一個元素
  • noneMatch 檢查是否沒有匹配的元素
  • findFirst 返回第一個元素
  • findAny 返回當前流中的任意元素
  • count 返回流中元素的總個數
  • max 返回流中最大值
  • min 返回流中最小值

基礎數據

List<Employee> emps = Arrays.asList(new Employee(1, "a1", 28, 3888.99, Employee.Status.BUSY),new Employee(2, "a2", 49, 336.66, Employee.Status.FREE),new Employee(3, "a3", 18, 3323.33, Employee.Status.VOCATION),new Employee(4, "a4", 38, 6666.77, Employee.Status.FREE),new Employee(5, "a5", 8, 80.88, Employee.Status.VOCATION),new Employee(6, "a6", 56, 100.66, Employee.Status.BUSY)
);

1. allMatch 檢查是否匹配所有元素

@Test
public void t1() {boolean bool = emps.stream().allMatch((e) -> e.getStatus().equals(Employee.Status.BUSY));System.out.println(bool);
}

2. anyMatch 檢查是否至少匹配一個元素

@Test
public void t2() {boolean bool = emps.stream().anyMatch((e) -> e.getStatus().equals(Employee.Status.BUSY));System.out.println(bool);
}

3. noneMatch 檢查是否沒有匹配的元素

@Test
public void t3() {boolean bool = emps.stream().noneMatch((e) -> e.getStatus().equals(Employee.Status.BUSY));System.out.println(bool);
}

4. findFirst 返回第一個元素

@Test
public void t4() {Optional<Employee> op = emps.stream().sorted((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary())).findFirst();System.out.println(op.get());
}

5. findAny 返回當前流中的任意元素

@Test
public void t5() {Optional<Employee> op = emps.stream().filter((e) -> e.getStatus().equals(Employee.Status.FREE)).findAny();System.out.println(op.get());
}

6. count 返回流中元素的總個數

/*** 查詢空閑人數*/
@Test
public void t6() {Long count = emps.stream().filter((e) -> e.getStatus().equals(Employee.Status.FREE)).count();System.out.println("count : " + count);
}

7. max 返回流中最大值

/*** 查詢工資最高的人*/
@Test
public void t7() {Optional<Employee> op = emps.stream().max((e1,e2) -> Double.compare(e1.getSalary(),e2.getSalary()));System.out.println(op.get());
}

8. min 返回流中最小值

/*** 獲取工資最少的人的工資*/
@Test
public void t8() {Optional<Double> op = emps.stream().map(Employee::getSalary).min(Double::compare);System.out.println(op.get());
}

4.2 規約(reduce)

  • T reduce(T identity, BinaryOperator<T> accumulator) 可以將流中的元素反復結合起來,得到一個值,返回 T
  • Optional<T> reduce(BinaryOperator<T> accumulator) 可以將流中的元素反復結合起來,得到一個值,返回 Optional<T>
備注:map 和 reduce 的連接通常稱為 map-reduce 模式,因 Google 用它來進行網絡搜索而出名

1. 實例

@Test
public void t9() {List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);Integer sum = list.stream().reduce(0, (x, y) -> x + y);System.out.println("sum : " + sum);
}
說明 :
首先將起始值 0 給 x,然后在流中取出一個元素 1 給了 y,然后 x y 相加結果為 1,再賦給 x,然后再取出一個元素 2 賦給y,然后 x y 相加結果為 3,以此類推

2. 實例

/*** 計算所有人工資的總和*/
@Test
public void t10() {Optional<Double> op = emps.stream().map(Employee::getSalary).reduce(Double::sum);System.out.println("Salary Sum : " + op.get());
}

4.3 收集(collect)

  • collect 將流轉換為其它形式,接收一個 Collector(收集器) 接口的實現,用于給 Stream 中元素做匯總的方法

Collector 接口中方法的實現決定了如何對流執行收集操作(如收集到List、Set、Map)。但是 Collector 實現類提供了很多靜態方法,可以方便地創建常見收集器實例,具體方法與實例如下表:

1. 實例 - 將結果收集到 List、Set 等容器

@Test
public void t1() {List<String> list = emps.stream().map(Employee::getName).collect(Collectors.toList());list.forEach(System.out::println);System.out.println("------------------------------------------");Set<String> set = emps.stream().map(Employee::getName).collect(Collectors.toSet());set.forEach(System.out::println);System.out.println("------------------------------------------");HashSet<String> hs = emps.stream().map(Employee::getName).collect(Collectors.toCollection(HashSet::new));hs.forEach(System.out::println);
}

2. 實例 - 計算

@Test
public void t2() {Long count = emps.stream().collect(Collectors.counting());System.out.println("總數 : " + count);System.out.println("------------------------------------------");Double avg = emps.stream().collect(Collectors.averagingDouble(Employee::getSalary));System.out.println("工資平均值 : " + avg);System.out.println("------------------------------------------");Double sum = emps.stream().collect(Collectors.summingDouble(Employee::getSalary));System.out.println("工資總和 : " + sum);System.out.println("------------------------------------------");Optional<Employee> max = emps.stream().collect(Collectors.maxBy((e1,e2) -> Double.compare(e1.getSalary(),e2.getSalary())));System.out.println("工資最多的員工 : " + max.get());System.out.println("------------------------------------------");Optional<Double> min = emps.stream().map(Employee::getSalary).collect(Collectors.minBy(Double::compare));System.out.println("工資最少的員工 : " + min.get());}

3. 實例 - 計算的另一種實現方式

@Test
public void t6() {DoubleSummaryStatistics dss = emps.stream().collect(Collectors.summarizingDouble(Employee::getSalary));System.out.println("sum : " + dss.getSum());System.out.println("max : " + dss.getMax());System.out.println("avg : " + dss.getAverage());System.out.println("count : " + dss.getCount());System.out.println("min : " + dss.getMin());
}

4.4 分組

分組就相當于 SQL 語句中的 group by,按一個類別或多個類別進行分組

1. 實例

@Test
public void t3() {Map<Employee.Status, List<Employee>> map = emps.stream().collect(Collectors.groupingBy(Employee::getStatus));// 格式化輸出,方便查看Gson gson = new GsonBuilder().setPrettyPrinting().create();System.out.println(gson.toJson(map));
}

2. 實例 多級分組

 @Test
public void t4() {Map<Employee.Status, Map<String, List<Employee>>> map = emps.stream().collect(Collectors.groupingBy(Employee::getStatus, Collectors.groupingBy((e) -> {if (e.getAge() <= 35) {return "青年";} else if (e.getAge() <= 50) {return "中年";} else {return "老年";}})));// 格式化輸出,方便查看Gson gson = new GsonBuilder().setPrettyPrinting().create();System.out.println(gson.toJson(map));
}

4.5 分區

分區是一種特殊的分組,結果 map 至少包含兩個不同的分組一個true,一個false

@Test
public void t5() {Map<Boolean,List<Employee>> map = emps.stream().collect(Collectors.partitioningBy((e) -> e.getSalary() > 1000));// 格式化輸出,方便查看Gson gson = new GsonBuilder().setPrettyPrinting().create();System.out.println(gson.toJson(map));
}

4.6 連接

將結果進行連接

@Test
public void t8() {String s1 = emps.stream().map(Employee::getName).collect(Collectors.joining());System.out.println("連接 : " + s1);String s2 = emps.stream().map(Employee::getName).collect(Collectors.joining(","));System.out.println("添加中間分隔符 : " + s2);String s3 = emps.stream().map(Employee::getName).collect(Collectors.joining(",", "==", "=="));System.out.println("添加左右分隔符 : " + s3);
}
本文首發于凌風博客:Java 8 新特性之Stream API
作者:凌風

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/388848.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/388848.shtml
英文地址,請注明出處:http://en.pswp.cn/news/388848.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

[Python設計模式] 第17章 程序中的翻譯官——適配器模式

github地址:https://github.com/cheesezh/python_design_patterns 適配器模式 適配器模式&#xff0c;將一個類的接口轉換成客戶希望的另外一個接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些類可以一起工作[DP]。 當系統的數據和行為都正確&#xff0c;但是接口…

Ubuntu中NS2安裝詳細教程

前言&#xff1a; NS2是指 Network Simulator version 2&#xff0c;NS&#xff08;Network Simulator&#xff09; 是一種針對網絡技術的源代碼公開的、免費的軟件模擬平臺&#xff0c;研究人員使用它可以很容易的進行網絡技術的開發&#xff0c;而且發展到今天&#xff0c;它…

es6核心特性圖

轉載于:https://juejin.im/post/5c19e188e51d452db4753925

帶你利用一句話完成轉場動畫

這篇文章主要給大家介紹了關于iOS如何利用一句話完成轉場動畫的相關資料&#xff0c;文中通過示例代碼介紹的非常詳細&#xff0c;對大家的學習或者工作具有一定的參考學習價值&#xff0c;需要的朋友們下面來一起學習學習吧前言本文介紹SS_AnimationTransition 的使用方法,利用…

14.vue路由腳手架

一.vue路由&#xff1a;https://router.vuejs.org/zh/ 1、定義 let router new VueRouter({mode:"history/hash",base:"基本路徑" 加一些前綴 必須在history模式下有效linkActiveClass:"active", 范圍選擇linkExactActiveClass:"exact&qu…

工程師、產品經理、數據工程師是如何一起工作的?

做為一名工程師&#xff0c;免不了與產品經理打交道&#xff0c;如果公司大一些&#xff0c;數據量多一些&#xff0c;還會有數據工程師這個角色。今天會和你主要聊一聊在工作中&#xff0c;產品經理和數據工程師在哪些方面對我們工程師的幫助最大&#xff0c;以及我從他們身上…

linux-buff/cache過大導致內存不足-程序異常

2019獨角獸企業重金招聘Python工程師標準>>> 問題描述 Linux內存使用量超過閾值&#xff0c;使得Java應用程序無可用內存&#xff0c;最終導致程序崩潰。即使在程序沒有掛掉時把程序停掉&#xff0c;系統內存也不會被釋放。 找原因的過程 這個問題已經困擾我好幾個月…

Android 適配(一)

一、Android適配基礎參數1.常見分辨率&#xff08;px&#xff09;oppx 2340x1080oppR15 2280x1080oppor11sp 2160*10801080*1920 (主流屏幕16&#xff1a;9)1080*216018:9 手機主流分辨率&#xff1a; 1080*2160高端 16:9 手機主流分辨率&#xff1a; 1080P (1080*1920) 或 2K …

Source Insight 創建工程(linux-2.6.22.6內核源碼)

1. 軟件設置 安裝完Source Insight&#xff0c;需要對其進行設置添加對“.S”匯編文件的支持&#xff1a; 2. 新建linux-2.6.22.6工程 1&#xff09;選擇工程存放的路徑&#xff1a; 2&#xff09;下載linux-2.6.22.6內核源碼&#xff0c;并解壓。在Source Insight中 指定源碼的…

課時20:內嵌函數和閉包

目錄&#xff1a; 一、global關鍵字 二、內嵌函數 三、閉包 四、課時20課后習題及答案 ******************** 一、global關鍵字 ******************** 全局變量的作用域是整個模塊&#xff08;整個代碼段&#xff09;&#xff0c;也就是代碼段內所有的函數內部都可以訪問到全局…

從零開始學產品第六篇:更強大的測試,自動化測試和性能測試

本篇為【從零開始學產品】系列課第1章第5節歡迎到公眾號菜單欄&#xff0c;獲取產品經理課程更多資料 “測試就是拿點鼠標在電腦上瞎點&#xff0c;或者是用手機隨便戳幾下么&#xff1f;” “不&#xff0c;是有計劃有意圖的測試&#xff0c;比如說&#xff0c;邊界測試&#…

Get 了濾鏡、動畫、AR 特效,速來炫出你的短視頻開發特技!

在濾鏡美顏、搞怪特效、炫酷場景等各種新奇玩法驅動下&#xff0c;短視頻開始讓人上癮。 12 月 3 日&#xff0c;七牛云聯合八大短視頻特效平臺共同推出了中國短視頻開發者創意大賽&#xff08;China Short Video Contest&#xff09;&#xff0c;面向全國邀請廣大開發者&#…

匿名函數、冒泡排序,二分法, 遞歸

匿名函數 lambda 匿名函數 格式 lambda 參數&#xff1a;返回值 函數名統一叫lambda&#xff0c;最多只能寫一行普通的正常的函數 def func(n):return n * n lambda匿名函數寫法 a lambda n : n**2 print(a(3)) 當有多個返回值時suiyi lambda x, y : (1, 2) # 筆試題 …

Redis源碼剖析

Redis源碼剖析和注釋&#xff08;一&#xff09;---鏈表結構 Redis源碼剖析和注釋&#xff08;二&#xff09;--- 簡單動態字符串 Redis源碼剖析和注釋&#xff08;三&#xff09;--- Redis 字典結構 Redis源碼剖析和注釋&#xff08;四&#xff09;--- 跳躍表(skiplist) Redis…

Android Activity生命周期

Android生命周期 Android的生命周期&#xff1a;onCreate() -> onStart() -> onResume() -> onPause() -> onStop() -> onDestroy() 如下圖所示&#xff1a; 1.當activity啟動時系統會先調用onCreate(),然后調用onStart(),最后調用**onResume()**方法&#xff0…

date數據存入mysql_Date對象存入mysql數據庫

java.sql.Date,java.sql.Time和java.sql.Timestamp三個都是java.util.Date的子類(包裝類)。java.sql.Date是java.util.Date的子類&#xff0c;是一個包裝了毫秒值的瘦包裝器&#xff0c;允許 JDBC 將毫秒值標識為 SQL DATE 值。毫秒值表示自 1970 年 1 月 1 日 00:00:00 GMT 以…

盛嚴謹,嚴謹,再嚴謹。_評估員工調查的統計嚴謹性

盛嚴謹,嚴謹,再嚴謹。The human resources industry relies heavily on a wide range of assessments to support its functions. In fact, to ensure unbiased and fair hiring practices the US department of labor maintains a set of guidelines (Uniform Guidelines) to …

復權就是對股價和成交量進行權息修

* 所謂復權就是對股價和成交量進行權息修復,按照股票的實際漲跌繪制股價走勢圖, * 并把成交量調整為相同的股本口徑。股票除權、除息之后&#xff0c;股價隨之產生了變化&#xff0c; * 但實際成本并沒有變化。 * 如&#xff1a;原來20元的股票&#xff0c;十送十之…

前端校驗和后端校驗

問&#xff1a;到底是前端校驗好還是后端校驗好呢&#xff1f; 答&#xff1a;后端校驗比前端校驗更安全&#xff0c;更可靠&#xff0c;前端校驗可以增加用戶體驗&#xff0c;一般來說&#xff0c;在前端校驗的東西在后端也必須校驗&#xff08;比如登陸用戶名、密碼&#xff…

[MySQL] INFORMATION_SCHEMA 數據庫包含所有表的字段

sql注入后可以通過該數據庫獲取所有表的字段信息1. COLLATIONS表 提供有關每個字符集的排序規則的信息。 COLLATIONS表包含以下列&#xff1a;COLLATION_NAME 排序規則名稱。 CHARACTER_SET_NAME 與排序規則關聯的字符集的名稱。 ID 排序規則ID。 IS_DEFAULT 排序規則是否為其字…