# Stream API 上
## 使用流
### 創建流
在使用流之前,首先需要擁有一個數據源,并通過StreamAPI提供的一些方法獲取該數據源的流對象。數據源可以有多種形式:
**1. 集合**
這種數據源較為常用,通過stream()方法即可獲取流對象:
```java
List<Person> list = new ArrayList<Person>();
Stream<Person> stream = list.stream();
```
**2. 數組**
通過Arrays類提供的靜態函數stream()獲取數組的流對象:
```java
String[] names = {"chaimm","peter","john"};
Stream<String> stream = Arrays.stream(names);
```
**3. 值**
直接將幾個值變成流對象:
```java
Stream<String> stream = Stream.of("chaimm","peter","john");
```
**4. 文件**
```java
try(Stream lines = Files.lines(Paths.get(“文件路徑名”),Charset.defaultCharset())){
//可對lines做一些操作
}catch(IOException e){
}
```
**5. iterator**
**創建無限流**
```java
Stream.iterate(0, n -> n + 2)
.limit(10)
.forEach(System.out::println);
```
> PS:Java7簡化了IO操作,把打開IO操作放在try后的括號中即可省略關閉IO的代碼。
### 篩選 filter
filter 函數接收一個Lambda表達式作為參數,該表達式返回boolean,在執行過程中,流將元素逐一輸送給filter,并篩選出執行結果為true的元素。
如,篩選出所有學生:
```java
List<Person> result = list.stream()
.filter(Person::isStudent)
.collect(toList());
```
### 去重distinct
去掉重復的結果:
```java
List<Person> result = list.stream()
.distinct()
.collect(toList());
```
### 截取
截取流的前N個元素:
```java
List<Person> result = list.stream()
.limit(3)
.collect(toList());
```
### 跳過
跳過流的前n個元素:
```java
List<Person> result = list.stream()
.skip(3)
.collect(toList());
```
### 映射
對流中的每個元素執行一個函數,使得元素轉換成另一種類型輸出。流會將每一個元素輸送給map函數,并執行map中的Lambda表達式,最后將執行結果存入一個新的流中。
如,獲取每個人的姓名(實則是將Perosn類型轉換成String類型):
```java
List<Person> result = list.stream()
.map(Person::getName)
.collect(toList());
```
### 合并多個流
例:列出List中各不相同的單詞,List集合如下:
```java
List<String> list = new ArrayList<String>();
list.add("I am a boy");
list.add("I love the girl");
list.add("But the girl loves another girl");
```
思路如下:
首先將list變成流:
```java
list.stream();
```
按空格分詞:
```java
list.stream()
.map(line->line.split(" "));
```
分完詞之后,每個元素變成了一個String[]數組。
將每個 `String[]` 變成流:
```java
list.stream()
.map(line->line.split(" "))
.map(Arrays::stream)
```
此時一個大流里面包含了一個個小流,我們需要將這些小流合并成一個流。
將小流合并成一個大流:用 `flatMap` 替換剛才的 map
```java
list.stream()
.map(line->line.split(" "))
.flatMap(Arrays::stream)
```
去重
```java
list.stream()
.map(line->line.split(" "))
.flatMap(Arrays::stream)
.distinct()
.collect(toList());
```
### 是否匹配任一元素:anyMatch
anyMatch用于判斷流中是否存在至少一個元素滿足指定的條件,這個判斷條件通過Lambda表達式傳遞給anyMatch,執行結果為boolean類型。
如,判斷list中是否有學生:
```java
boolean result = list.stream()
.anyMatch(Person::isStudent);
```
### 是否匹配所有元素:allMatch
allMatch用于判斷流中的所有元素是否都滿足指定條件,這個判斷條件通過Lambda表達式傳遞給anyMatch,執行結果為boolean類型。
如,判斷是否所有人都是學生:
```java
boolean result = list.stream()
.allMatch(Person::isStudent);
```
### 是否未匹配所有元素:noneMatch
noneMatch與allMatch恰恰相反,它用于判斷流中的所有元素是否都不滿足指定條件:
```java
boolean result = list.stream()
.noneMatch(Person::isStudent);
```
### 獲取任一元素findAny
findAny能夠從流中隨便選一個元素出來,它返回一個Optional類型的元素。
```java
Optional<Person> person = list.stream().findAny();
```
### 獲取第一個元素findFirst
```java
Optional<Person> person = list.stream().findFirst();
```
### 歸約
歸約是將集合中的所有元素經過指定運算,折疊成一個元素輸出,如:求最值、平均數等,這些操作都是將一個集合的元素折疊成一個元素輸出。
在流中,reduce函數能實現歸約。
reduce函數接收兩個參數:
1. 初始值
2. 進行歸約操作的Lambda表達式
**元素求和:自定義Lambda表達式實現求和**
例:計算所有人的年齡總和
~~~
@Test
public void contextLoads() {
List<Person> list = new ArrayList<>();
list.add(new Person().setAge(20));
list.add(new Person().setAge(25));
int age = list.stream().map(Person::getAge).reduce(0, Integer::sum);
System.out.println(age);
}
~~~
~~~
@Data
@Accessors(chain = true)
class Person {
private int age;
}
~~~
1. reduce的第一個參數表示初試值為0;
2. reduce的第二個參數為需要進行的歸約操作,它接收一個擁有兩個參數的Lambda表達式,reduce會把流中的元素兩兩輸給Lambda表達式,最后將計算出累加之和。
**元素求和:使用Integer.sum函數求和**
上面的方法中我們自己定義了Lambda表達式實現求和運算,如果當前流的元素為數值類型,那么可以使用Integer提供了sum函數代替自定義的Lambda表達式,如:
```java
int age = list.stream().reduce(0, Integer::sum);
```
Integer類還提供了 `min`、`max` 等一系列數值操作,當流中元素為數值類型時可以直接使用。
### 數值流的使用
采用reduce進行數值操作會涉及到基本數值類型和引用數值類型之間的裝箱、拆箱操作,因此效率較低。
當流操作為純數值操作時,使用數值流能獲得較高的效率。
**將普通流轉換成數值流**
StreamAPI提供了三種數值流:IntStream、DoubleStream、LongStream,也提供了將普通流轉換成數值流的三種方法:mapToInt、mapToDouble、mapToLong。
如,將Person中的age轉換成數值流:
```java
IntStream stream = list.stream().mapToInt(Person::getAge);
```
**數值計算**
每種數值流都提供了數值計算函數,如max、min、sum等。如,找出最大的年齡:
```java
OptionalInt maxAge = list.stream()
.mapToInt(Person::getAge)
.max();
```
由于數值流可能為空,并且給空的數值流計算最大值是沒有意義的,因此max函數返回OptionalInt,它是Optional的一個子類,能夠判斷流是否為空,并對流為空的情況作相應的處理。
此外,mapToInt、mapToDouble、mapToLong進行數值操作后的返回結果分別為:OptionalInt、OptionalDouble、OptionalLong
## 中間操作和收集操作
| 操作 | 類型 | 返回類型 | 使用的類型/函數式接口 | 函數描述符 |
|:-----:|:--------|:-------|:-------|:-------|
| `filter` | 中間 | `Stream<T>` | `Predicate<T>` | `T -> boolean` |
| `distinct` | 中間 | `Stream<T>` | | |
| `skip` | 中間 | `Stream<T>` | long | |
| `map` | 中間 | `Stream<R>` | `Function<T, R>` | `T -> R` |
| `flatMap` | 中間 | `Stream<R>` | `Function<T, Stream<R>>` | `T -> Stream<R>` |
| `limit` | 中間 | `Stream<T>` | long | |
| `sorted` | 中間 | `Stream<T>` | `Comparator<T>` | `(T, T) -> int` |
| `anyMatch` | 終端 | `boolean` | `Predicate<T>` | `T -> boolean` |
| `noneMatch` | 終端 | `boolean` | `Predicate<T>` | `T -> boolean` |
| `allMatch` | 終端 | `boolean` | `Predicate<T>` | `T -> boolean` |
| `findAny` | 終端 | `Optional<T>` | | |
| `findFirst` | 終端 | `Optional<T>` | | |
| `forEach` | 終端 | `void` | `Consumer<T>` | `T -> void` |
| `collect` | 終端 | `R` | `Collector<T, A, R>` | |
| `reduce` | 終端 | `Optional<T>` | `BinaryOperator<T>` | `(T, T) -> T` |
| `count` | 終端 | `long` | | |
- 第零章 序
- 序言
- 系統架構
- 視頻公開課
- 開源版介紹
- 商業版介紹
- 功能對比
- 答疑流程
- 第一章 快速開始
- 升級必看
- 環境要求
- 環境準備
- 基礎環境安裝
- Docker安裝基礎服務
- Nacos安裝
- Sentinel安裝
- 插件安裝
- 建數據庫
- 工程導入
- 導入Cloud版本
- 導入Nacos配置
- 導入Boot版本
- 工程運行
- 運行Cloud版本
- 運行Boot版本
- 工程測試
- 測試Cloud版本
- 測試Boot版本
- 第二章 技術基礎
- Java
- Lambda
- Lambda 受檢異常處理
- Stream 簡介
- Stream API 一覽
- Stream API (上)
- Stream API (下)
- Optional 干掉空指針
- 函數式接口
- 新的日期 API
- Lombok
- SpringMVC
- Swagger
- Mybatis
- Mybatis-Plus
- 開發規范
- 第三章 開發初探
- 新建微服務工程
- 第一個API
- API鑒權
- API響應結果
- Redis緩存
- 第一個CRUD
- 建表
- 建Entity
- 建Service和Mapper
- 新增 API
- 修改 API
- 刪除 API
- 查詢 API
- 單條數據
- 多條數據
- 分頁
- 微服務遠程調用
- 聲明式服務調用 Feign
- 熔斷機制 Hystrix
- 第四章 開發進階
- 聚合文檔
- 鑒權配置
- 跨域處理
- Xss防注入
- 自定義啟動器
- Secure安全框架
- Token認證簡介
- Token認證配置
- PreAuth注解配置
- Token認證實戰
- Token認證加密
- 日志系統
- 原理解析
- 功能調用
- Seata分布式事務
- 簡介
- 編譯包啟動
- 配置nacos對接
- docker啟動
- 對接微服務
- 代碼生成配置
- 前言
- 數據庫建表
- 代碼生成
- 前端配置
- 優化效果
- 第五章 功能特性
- SaaS多租戶
- 概念
- 數據隔離配置
- 線程環境自定義租戶ID
- 多終端令牌認證
- 概念
- 系統升級
- 使用
- 第三方系統登錄
- 概念說明
- 對接說明
- 對接準備
- 配置說明
- 操作流程
- 后記
- UReport2報表
- 報表簡介
- 對接配置
- 報表后記
- 接口報文加密
- 簡介
- 運行邏輯
- 對接準備
- 功能配置
- 接口測試
- 改造查詢
- 改造提交
- 改造刪除
- 動態數據權限
- 數據權限簡介
- 數據權限開發
- 純注解配置
- Web全自動配置
- 注解半自動配置
- 數據權限注意點
- 動態接口權限
- 樂觀鎖配置
- 統一服務登陸配置
- Skywalking追蹤監控
- Minio分布式對象存儲
- Boot版本對接至Cloud
- 第六章 生產部署
- windows部署
- linux部署
- jar部署
- docker部署
- java環境安裝
- mysql安裝
- docker安裝
- docker-compose安裝
- harbor安裝
- 部署步驟
- 寶塔部署
- 準備工作
- 安裝工作
- 部署準備
- 部署后端
- 部署前端
- 部署域名
- 結束工作
- k8s平臺部署
- 第七章 版本控制
- Git遠程分支合并
- Git地址更換
- 第八章 學習資料
- 第九章 FAQ
- 第十章 聯系我們