什麼是流
Java 8 API添加了一個新的抽象稱爲流Stream,其可以以一種聲明式的方式處理數據。它將基礎操作鏈接起來,能完成複雜的數據處理流水線,並提供了透明的並行處理機制。
Stream流的組成
- 數據源
- 中間操作
- 終端操作
一圖勝千言,從整體上了解下相關的內容
流與集合的區別:
- 流相對於是時間上的集合,得到的是某一時刻的數據(面向計算)
- 集合相對於是空間上的集合,需要真實的存儲(面向存儲)
- 流只能遍歷一次,集合可以反覆操作
- 流是內部迭代,集合是外部迭代
怎麼使用流編程
下面將對流編程的操作和構建一一進行演示
創建一個實體類User方便我們使用
class User {
private Integer userId;
private String userName;
private String password;
public User(Integer userId, String userName, String password) {
this.userId = userId;
this.userName = userName;
this.password = password;
}
public Integer getUserId() {
return userId;
}
public String getUserName() {
return userName;
}
public String getPassword() {
return password;
}
}
創建一個集合,作爲我們數據操作的數據
List<User> list = new ArrayList<User>() {
{
add(new User(5,"ckhhh", "123123"));
add(new User(1,"zhangsan", "1232312"));
add(new User(3,"lisi", "000000"));
add(new User(2,"lisi", "4324324"));
add(new User(4,"wangwu", "99999"));
}
};
中間操作
- 無狀態的
- filter使用
/**
* filter使用 按照指定條件過濾集合
*/
list.stream().
filter(user -> user.getUserName().equals("lisi")).
forEach(user ->
System.out.println(JSON.toJSONString(user, true)));
打印用戶名爲lisi的用戶的信息,結果如下:
{
"password":"000000",
"userId":3,
"userName":"lisi"
}
{
"password":"4324324",
"userId":2,
"userName":"lisi"
}
- map使用
/**
* map使用 從一種類型轉化爲另一種類型的集合,user->string
*/
list.stream().
map(user -> user.getUserName()).
forEach(user ->
System.out.println(JSON.toJSONString(user, true)));
使用map將集合分割,只獲取所有用戶並打印,結果如下:
"ckhhh"
"zhangsan"
"lisi"
"lisi"
"wangwu"
- flatMap操作
/**
* flatMap操作 接收一個元素,返回一個新的流(將對象轉換爲流操作)
*/
list.stream()
.flatMap(user -> Arrays.stream(user.getUserName().split("")))
.forEach(user ->
System.out.println(JSON.toJSONString(user, true)));
使用flatMap將集合變爲新的流:將名字分割成字符,然後打印,結果如下:
"c"
"k"
"h"
"h"
"h"
"z"
"h"
"a"
"n"
"g"
"s"
"a"
"n"
"l"
"i"
"s"
"i"
"l"
"i"
"s"
"i"
"w"
"a"
"n"
"g"
"w"
"u"
- peek操作
/**
* peek 中間操作 無狀態的 操作完後流還可以使用 foreach使用後就不能用了
* peek和foreach交替打印
*/
list.stream()
.peek(user -> System.out.println(user.getUserName()))
.forEach(user ->
System.out.println(JSON.toJSONString(user, true)));
打印用戶名,因爲是無狀態的,所以會執行完一下peek後執行一下foreach中的打印,結果如下
ckhhh
{
"password":"123123",
"userId":5,
"userName":"ckhhh"
}
zhangsan
{
"password":"1232312",
"userId":1,
"userName":"zhangsan"
}
lisi
{
"password":"000000",
"userId":3,
"userName":"lisi"
}
lisi
{
"password":"4324324",
"userId":2,
"userName":"lisi"
}
wangwu
{
"password":"99999",
"userId":4,
"userName":"wangwu"
}
- 有狀態的
- sort操作
/**
* sort操作,對數據按照規則排序
* sorted是有狀態的 所以會等peek結束之後再操作
* peek和foreach就不會再交替打印了
*/
list.stream()
.peek(user -> System.out.println(user.getUserName()))
.sorted(Comparator.comparing(User::getUserId))
.forEach(user ->
System.out.println(JSON.toJSONString(user, true)));
排序後打印,是有狀態的,所以必須所有peek執行完了後才能執行,根據userId升序排列,結果如下:
ckhhh
zhangsan
lisi
lisi
wangwu
{
"password":"1232312",
"userId":1,
"userName":"zhangsan"
}
{
"password":"4324324",
"userId":2,
"userName":"lisi"
}
{
"password":"000000",
"userId":3,
"userName":"lisi"
}
{
"password":"99999",
"userId":4,
"userName":"wangwu"
}
{
"password":"123123",
"userId":5,
"userName":"ckhhh"
}
- distinct操作
/**
* distinct操作 去除重複
*/
list.stream()
.map(user -> user.getUserName())
.distinct()
.forEach(user ->
System.out.println(JSON.toJSONString(user, true)));
去除用戶名中的重複項後打印用戶名,結果如下:
"ckhhh"
"zhangsan"
"lisi"
"wangwu"
- skip操作
/**
* skip過濾掉前三條
*/
list.stream()
.sorted(Comparator.comparing(user ->user.getUserId()))
.skip(3)
.forEach(user ->
System.out.println(JSON.toJSONString(user, true)));
按id升序排序後,跳過前三條,從第四條開始打印,結果如下:
{
"password":"99999",
"userId":4,
"userName":"wangwu"
}
{
"password":"123123",
"userId":5,
"userName":"ckhhh"
}
- limit操作
/**
* limit操作 獲取前三條
*/
list.stream()
.sorted(Comparator.comparing(user ->user.getUserId()))
.limit(3)
.forEach(user ->
System.out.println(JSON.toJSONString(user, true)));
按id升序排列後,打印前三條,結果如下:
{
"password":"1232312",
"userId":1,
"userName":"zhangsan"
}
{
"password":"4324324",
"userId":2,
"userName":"lisi"
}
{
"password":"000000",
"userId":3,
"userName":"lisi"
}
終端操作
- 短路的操作
- allMatch操作
/**
* allMatch 使用:是否全部滿足終端操作,短路的操作
* 如果發現了不匹配的 就不會繼續執行了,peek中打印的數據打不符合的就停止了
*/
boolean b = list.stream()
.peek(user -> System.out.println(user.getUserId()))
.allMatch(user->user.getUserId()> 2);
System.out.println(b);
判斷是否所有的用戶id都大於2,一旦發現小於等於2的就返回false,結果如下
5
1
false
- anyMatch操作
/**
* anyMatch使用 :只要有滿足的,就返回true
* 短路的 找到一個符合的就返回true
*/
boolean c = list.stream()
.peek(user -> System.out.println(user.getUserId()))
.anyMatch(user->user.getUserId()> 2);
System.out.println(c);
判斷是否有用戶id大於2,一旦發現大於2的就返回true,結果如下
5
true
- noneMatch操作
/**
* noneMatch使用:所有的都沒匹配上 返回true
* 短路的,找到一個符合的就返回false
*/
boolean d = list.stream()
.peek(user -> System.out.println(user.getUserId()))
.noneMatch(user->user.getUserId()> 2);
System.out.println(d);
判斷是否沒有用戶id大於2,一旦發現大於2的就返回false,結果如下:
5
false
- findFirst操作
/**
* findFirst:獲取第一個對象
* 短路的 獲取了就返回
*/
Optional<User> first = list.stream()
.findFirst();
System.out.println(JSON.toJSONString(first.get(), true));
返回第一個對象,結果如下
{
"password":"123123",
"userId":5,
"userName":"ckhhh"
}
- findAny操作
/**
* findAny找到任何一個就返回
* 並行操作時findAny會更快,可能會隨機匹配 如果串行一致
*/
Optional<User> first1 = list.stream()
.findAny();
System.out.println(JSON.toJSONString(first1.get(), true));
返回任意一個對象,在串行下和返回第一個對象一樣,在並行下就是隨機的,結果如下:
{
"password":"123123",
"userId":5,
"userName":"ckhhh"
}
- 非短路操作
- max操作
/**
* max操作:獲取最大值
*/
OptionalDouble max = list.stream()
.mapToDouble(User::getUserId)
.max();
System.out.println(max.getAsDouble());
將用戶id列分割出來,將值轉爲doble,返回最大值,結果如下:
5.0
- min操作
/**
* min操作:獲取最小值
*/
OptionalDouble min = list.stream()
.mapToDouble(User::getUserId)
.min();
System.out.println(min.getAsDouble());
將用戶id列分割出來,將值轉爲doble,返回最小值,結果如下:
1.0
- count操作
/**
* count操作:獲取元素的個數
*/
long count = list.stream()
.count();
System.out.println(count);
統計元素個數,結果如下:
5
- collect操作
/**
* 集合收集器
*/
List<User> result = list.stream()
.filter(user -> user.getUserId() > 2)
.collect(Collectors.toList());
System.out.println(JSON.toJSONString(result, true));
將id大於2的元素重新寫入集合,打印結果如下:
[
{
"password":"123123",
"userId":5,
"userName":"ckhhh"
},
{
"password":"000000",
"userId":3,
"userName":"lisi"
},
{
"password":"99999",
"userId":4,
"userName":"wangwu"
}
]
/**
* 分組收集器
*/
Map<String, List<User>> group = list.stream()
.collect(Collectors.groupingBy(
user -> user.userName));
System.out.println(JSON.toJSONString(group, true));
將元素按用戶名分組後打印,結果如下:
{
"lisi":[
{
"password":"000000",
"userId":3,
"userName":"lisi"
},
{
"password":"4324324",
"userId":2,
"userName":"lisi"
}
],
"ckhhh":[
{
"password":"123123",
"userId":5,
"userName":"ckhhh"
}
],
"zhangsan":[
{
"password":"1232312",
"userId":1,
"userName":"zhangsan"
}
],
"wangwu":[
{
"password":"99999",
"userId":4,
"userName":"wangwu"
}
]
}
/**
* 分區收集器
* 分爲大於2和小於等於2兩部分
*/
Map<Boolean, List<User>> partition = list.stream()
.collect(Collectors.partitioningBy(user -> user.getUserId() > 2));
System.out.println(JSON.toJSONString(partition, true));
將元素按id大於2位標準分爲兩個區後進行打印,結果如下:
{false:[
{
"password":"1232312",
"userId":1,
"userName":"zhangsan"
},
{
"password":"4324324",
"userId":2,
"userName":"lisi"
}
],true:[
{
"password":"123123",
"userId":5,
"userName":"ckhhh"
},
{
"password":"000000",
"userId":3,
"userName":"lisi"
},
{
"password":"99999",
"userId":4,
"userName":"wangwu"
}
]
}
構建流
- 由數值直接構建流
public void steamFromValue() {
Stream stream = Stream.of(1, 2, 3, 4, 5);
stream.forEach(System.out::println);
}
運行結果如下:
1
2
3
4
5
- 由數組構建流
public void streamFromArray() {
int[] num = new int[]{1, 2, 3, 4, 5};
Arrays.stream(num)
.forEach(System.out::println);
}
運行結果如下:
1
2
3
4
5
- 通過文件構建流
public void streamFromFile() throws IOException {
Stream<String> stream = Files.lines(Paths.get("F:/QQ下載/FileServer.java"));
stream.forEach(System.out::println);
}
運行結果如下(部分)
package com.demo.lambda;
import java.io.*;
/**
* 〈文件服務類〉
*
* @author Chkl
* @create 2020/4/20
* @since 1.0.0
*/
public class FileServer {
/**
* 通過url獲取本地文件內容
*
* @param url
* @param fileConsumer
*/
public void fileHandler(String url, FileConsumer fileConsumer) throws IOException {
..............
...............
- 通過函數生成流(無限流)
public void streamFromFunction() {
//參數: 初始值,規則(從0開始生成偶數)
Stream<Integer> stream = Stream.iterate(0, n -> n + 2);
stream.limit(100)
.forEach(System.out::println);
//隨機生成
Stream<Double> stream2 = Stream.generate(Math::random);
stream2.limit(100)
.forEach(System.out::println);
}
第一種運行結果如下(部分):
0
2
4
6
8
10
12
14
16
18
20
22
24
第二種運行結果如下(部分):
0.28047181698982915
0.3366379505477519
0.6314896823002529
0.4202209521644461
0.8362832087391032
0.7025533309035066
0.49208504805565545
0.18517793464530763
0.37580620856029856
0.8888281509032474
0.8399345540793907
0.6470816912713735
0.9842389606178953
0.5166615554196825
0.035214233629631164
通過以上的學習,可以基本的掌握Stream流常用的一些操作和構建方便了,具體業務需要使用到的方法在使用時再去了解