詳解Java中Stream流的用法和原理
一、創(chuàng)建不可變的集合
不可變的集合,顧名思義,就是不想讓別人修改集合中的內(nèi)容,也無(wú)法修改。如何創(chuàng)建呢?
(一)創(chuàng)建格式
在List、Set、Map接口中,都存在靜態(tài)的of方法,可以獲取一個(gè)不可變的集合:
- static List of(E...elements) 創(chuàng)建一個(gè)具有指定元素的List集合對(duì)象。
- static Set of(E...elements) 創(chuàng)建一個(gè)具有指定元素的Set集合對(duì)象。
- static <K , V> Map<K,V> of(E...elements) 創(chuàng)建一個(gè)具有指定元素的Map集合對(duì)象。
(List.of在jdk8中沒(méi)有,需要jdk9及以上的版本。)
public static void main(String[] args) {
List<String> list = List.of("張三","李四","王五");
for(String tmp : list){
System.out.println(tmp);
}
System.out.println("----------------------------");
Set<Integer> set = Set.of(1,2,3,4);
for(int x : set){
System.out.println(x);
}
System.out.println("-----------------------------");
Map<String, Integer> map = Map.of("王五", 1, "李四", 2);
Set<Map.Entry<String, Integer>> entries = map.entrySet();
for(Map.Entry<String, Integer> entry : entries){
System.out.println(entry.getKey() + ":" + entry.getValue());
}
System.out.println("------------------------------");
}
- 這些集合不能添加,不能刪除,不能修改,如果修改會(huì)拋異常:
public static void main(String[] args) {
List<String> list = List.of("張三","李四","王五");
list.remove(0);
}結(jié)果:

- 這里的Set集合中不能有相同的值,Map不能有相同的key,否則拋異常。
public static void main(String[] args) {
Set<Integer> set = Set.of(1,1,2,3,4);
}
Map.of()里的參數(shù)最多傳 10 個(gè)鍵值對(duì),源碼里of()方法參數(shù)最多的也就10對(duì)。

那為什么不用可變參數(shù)呢?因?yàn)榭勺儏?shù)只能有一個(gè),并且要在參數(shù)列表的末尾。如果想要存超過(guò)10個(gè)的鍵值對(duì),我們可以利用數(shù)組->可變參數(shù):
public static void main(String[] args) {
Map<Integer,String> map = new HashMap<>();
map.put(1,"a");
map.put(2,"b");
map.put(3,"c");
map.put(4,"d");
map.put(5,"e");
map.put(6,"f");
map.put(7,"g");
map.put(8,"h");
map.put(9,"i");
map.put(10,"j");
map.put(11,"k");
map.put(12,"l");
//獲取所有的鍵值對(duì)
Set<Map.Entry<Integer,String>> entries = map.entrySet();
//把entries變成一個(gè)數(shù)組
//如果集合的長(zhǎng)度 〉數(shù)組的長(zhǎng)度﹔數(shù)據(jù)在數(shù)組中放不下,此時(shí)會(huì)根據(jù)實(shí)際數(shù)據(jù)的個(gè)數(shù),重新創(chuàng)建數(shù)組
//如果集合的長(zhǎng)度〈=數(shù)組的長(zhǎng)度:數(shù)據(jù)在數(shù)組中放的下,此時(shí)不會(huì)創(chuàng)建新的數(shù)組,而是直接用原來(lái)的數(shù)組
Map.Entry[] array = entries.toArray(new Map.Entry[0]);
//生成不可變map集合,ofEntries()的參數(shù)是可變參數(shù),可變參數(shù)可以使用數(shù)組。
Map map1 = Map.ofEntries(array);
}第二種方式,但是要jdk10及以上:
public static void main(String[] args) {
Map<Integer,String> map = new HashMap<>();
map.put(1,"a");
map.put(2,"b");
map.put(3,"c");
map.put(4,"d");
map.put(5,"e");
map.put(6,"f");
map.put(7,"g");
map.put(8,"h");
map.put(9,"i");
map.put(10,"j");
map.put(11,"k");
map.put(12,"l");
//生成不可變集合
Map<Integer,String> map1 = Map.copyOf(map);
}(二)不可變集合的作用
- 保證線程安全:不可變集合是一種在創(chuàng)建之后就不再變更的對(duì)象,這種特性使得它們天生支持線程安全。
- 防止數(shù)據(jù)被篡改:不可變集合可以用于封裝一些敏感或重要的數(shù)據(jù),防止它們被外部程序或不可信的庫(kù)修改或破壞。
二、Stream 流
(一)Stream 的思想
Stream 為什么叫流?可以通過(guò)下面的案例來(lái)引入這個(gè)概念。
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("陳大帥");
list.add("陳小帥");
list.add("小紅");
list.add("李四");
list.add("陳四");
//篩選姓陳的,并且名字是3個(gè)字的人
list.stream().filter(name->name.startsWith("陳"))
.filter(name->name.length()==3)
.forEach(name-> System.out.println(name));
}Stream 就像流水線一樣,這個(gè)流水線第一道工序就是過(guò)濾掉不姓陳的,第二道工序過(guò)濾掉字的數(shù)量不等于3的,最后的工序就是打印。
(二)Stream流的使用步驟
- 得到一條Stream流水線,把數(shù)據(jù)放上去。
- 利用Stream流中的API進(jìn)行各種操作。
中間方法:方法調(diào)用后還可以調(diào)用其它方法,如上面的filter。
終結(jié)方法:最后一步,調(diào)用后不能調(diào)用其它方法。
1.把數(shù)據(jù)放到流水線上
| 獲取方式 | 方法名 | 說(shuō)明 |
|---|---|---|
| 單列集合 | default Stream stream() | collection中的默認(rèn)方法(由于單列集合實(shí)現(xiàn)了Collection接口,可以直接.stream()) |
| 雙列集合(Map等) | 無(wú) | 無(wú)法直接使用stream流 |
| 數(shù)組 | public static Stream stream(T[] array) | Arrays工具類中的靜態(tài)方法 |
| 零碎數(shù)據(jù) | public static Stream stream(T[] array) | Stream接口中的靜態(tài)方法 |
public static void main(String[] args) {
//1.單列集合
List<String> list = new ArrayList<>();
Collections.addAll(list,"a","b","b","b","c","d","e");//添加數(shù)據(jù)
list.stream().forEach(s -> System.out.println(s));//流水線操作
//2.雙列集合
Map<Integer,String> map = new HashMap<>();
map.put(1,"a");
map.put(2,"b");
map.put(3,"c");
map.put(4,"d");
map.put(5,"e");
map.put(6,"f");
map.keySet().stream().forEach(s-> System.out.println(s));//方式1:獲取key的流水線,利用key與value的關(guān)系來(lái)操作整體
map.entrySet().stream().forEach(entry-> System.out.println(entry));//方式2:獲取鍵值對(duì)的流水線
//3.數(shù)組
int[] arr = {1,2,3,4,5,6,7,8};
Arrays.stream(arr).forEach(x-> System.out.println(x));//利用 Arrays 工具類來(lái)獲取流
String[] strs = {"a","b","c"};
Arrays.stream(strs).forEach(str-> System.out.println(str));
//4.零散的數(shù)據(jù)
Stream.of("a","b","c","d","e").forEach(x-> System.out.println(x));//利用 Stream 來(lái)獲取
}2.中間方法
| Stream方法 | 中文解釋 | 作用 |
|---|---|---|
| Stream filter(Predicate<? super T> predicate) | 篩選 | 過(guò)濾出符合條件的元素 |
| Stream limit(long maxSize) | 限制 | 截取前maxSize個(gè)元素 |
| Stream skip(long n) | 跳過(guò) | 跳過(guò)前n個(gè)元素 |
| Stream distinct() | 去重 | 去除重復(fù)的元素(根據(jù)hashCode和equals方法) |
| static Stream concat(Stream a, Stream b) | 合并 | 將兩個(gè)Stream合并為一個(gè) |
| Stream map(Function<T, R> mapper) | 映射 | 將每個(gè)元素轉(zhuǎn)換為另一種類型或形式 |
注意1:每個(gè)中間方法都是返回新的Stream流,原來(lái)的Stream流只能使用一次,建議使用鏈?zhǔn)骄幊獭?/p>
注意2:修改Stream流中的數(shù)據(jù),不會(huì)影響原來(lái)集合或者數(shù)組中的數(shù)據(jù)。
- filter
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("陳大帥");
list.add("陳小帥");
list.add("小紅");
list.add("李四");
list.add("陳四");
//篩選姓陳的,并且名字是3個(gè)字的人
list.stream().filter(new Predicate<String>() {
@Override
public boolean test(String s) {
//如果返回值為true,表示當(dāng)前數(shù)據(jù)留下
//如果返回值為false,表示當(dāng)前數(shù)據(jù)舍棄不要
return s.startsWith("陳");
}
}).forEach(s -> System.out.println(s));//通過(guò)匿名內(nèi)部類
list.stream().filter(s->s.startsWith("陳")).forEach(s -> System.out.println(s));//通過(guò) lambda
}
- limit
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("陳大帥");
list.add("陳小帥");
list.add("小紅");
list.add("李四");
list.add("陳四");
//截取前 3 個(gè)數(shù)據(jù)
list.stream().limit(3).forEach(s -> System.out.println(s));
}
- skip
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("陳大帥");
list.add("陳小帥");
list.add("小紅");
list.add("李四");
list.add("陳四");
//跳過(guò)前 3 個(gè)數(shù)據(jù)
list.stream().skip(3).forEach(s -> System.out.println(s));
}
- distinct
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("陳大帥");
list.add("陳大帥");
list.add("陳大帥");
list.add("陳大帥");
list.add("陳小帥");
list.add("陳小帥");
list.add("陳小帥");
list.add("陳小帥");
list.add("小紅");
list.add("李四");
list.add("陳四");
//去重
list.stream().distinct().forEach(s -> System.out.println(s));
}
- concat
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("陳大帥");
list.add("陳小帥");
list.add("小紅");
list.add("李四");
list.add("陳四");
List<String> list2 = new ArrayList<>();
list2.add("a");
list2.add("b");
list2.add("c");
//合并,如果類型不相同,類型就會(huì)變?yōu)楣餐母割?
Stream.concat(list.stream(),list2.stream()).forEach(s -> System.out.println(s));
}
- 將每個(gè)元素轉(zhuǎn)換為另一種類型或形式
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("陳大帥-20");
list.add("陳小帥-19");
list.add("小紅-11");
list.add("李四-12");
list.add("陳四-13");
list.stream().map(new Function<String, Integer>() {
//第一個(gè)類型:流中原本的數(shù)據(jù)類型
// 第二個(gè)類型:要轉(zhuǎn)成之后的類型
@Override
public Integer apply(String s) {
//apply的形參s:依次表示流里面的每一個(gè)數(shù)據(jù)
//返回值:表示轉(zhuǎn)換之后的數(shù)據(jù)
String[] split = s.split("-");
int age = Integer.parseInt(split[1]);
return age;
}
}).forEach(age-> System.out.println(age));//內(nèi)部類
list.stream().map(s->Integer.parseInt(s.split("-")[1])).forEach(age-> System.out.println(age));//lambda
}
3.終結(jié)方法
| Stream方法 | 中文解釋 | 作用 |
|---|---|---|
| void forEach(Consumer action) | 遍歷 | 對(duì)每個(gè)元素執(zhí)行指定的操作 |
| long count() | 統(tǒng)計(jì) | 返回元素的個(gè)數(shù) |
| toArray() | 收集到數(shù)組 | 返回一個(gè)包含所有元素的數(shù)組 |
| collect(Collector collector) | 收集到集合 | 返回一個(gè)包含所有元素的集合,可以指定集合的類型和特性 |
- forEach
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("陳大帥-20");
list.add("陳小帥-19");
list.add("小紅-11");
list.add("李四-12");
list.add("陳四-13");
// consumer的泛型:表示流中數(shù)據(jù)的類型
list.stream().forEach(new Consumer<String>() {
//accept方法的形參s:依次表示流里面的每一個(gè)數(shù)據(jù)
@Override
public void accept(String s) {
System.out.println(s);
}
});
list.stream().forEach(s -> System.out.println(s));//lambda
}- count
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("陳大帥-20");
list.add("陳小帥-19");
list.add("小紅-11");
list.add("李四-12");
list.add("陳四-13");
//統(tǒng)計(jì)集合中的個(gè)數(shù)
long count = list.stream().count();
System.out.println(count);
}
- toArray
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("陳大帥-20");
list.add("陳小帥-19");
list.add("小紅-11");
list.add("李四-12");
list.add("陳四-13");
//方式一:
//收集集合中的數(shù)據(jù)到數(shù)組
Object[] array = list.stream().toArray();
System.out.println(Arrays.toString(array));
//方式二:指定對(duì)于類型
//IntFunction的泛型:具體類型的數(shù)組
//toArray方法的參數(shù):負(fù)責(zé)創(chuàng)建一個(gè)指定類型的數(shù)組
//toArray方法的底層:會(huì)依次得到流里面的每一個(gè)數(shù)據(jù),并把數(shù)據(jù)放到數(shù)組當(dāng)中
String[] strings = list.stream().toArray(new IntFunction<String[]>() {
/**
*
* @param value 流中數(shù)據(jù)的個(gè)數(shù),要跟數(shù)組長(zhǎng)度保持一致
* @return 具體類型的數(shù)組
*/
@Override
public String[] apply(int value) {
return new String[value];
}
});
System.out.println(Arrays.toString(strings));
//方式三:使用 lambda 表達(dá)式
String[] array1 = list.stream().toArray(value -> new String[value]);//lambda
System.out.println(Arrays.toString(array1));
}
- collect
收集到List、Set中:
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("陳大帥");
list.add("陳大帥");
list.add("陳大帥");
list.add("陳小帥");
list.add("陳小帥");
list.add("陳小帥");
list.add("小紅");
list.add("李四");
list.add("陳四");
//收集到list中
List<String> newList = list.stream().filter(s->s.startsWith("陳")).collect(Collectors.toList());
System.out.println(newList);
//收集到Set中,自動(dòng)去重
Set<String> set = list.stream().filter(s -> s.startsWith("陳")).collect(Collectors.toSet());
System.out.println(set);
}收集到Map中:
public static void main(String[] args) {
List<String> list2 = new ArrayList<>();
list2.add("陳大帥-20");
list2.add("陳小帥-12");
list2.add("小紅-15");
list2.add("李四-15");
list2.add("陳四-38");
//收集到Map中,鍵:姓名 值:年齡,注意:收集到Map中的時(shí)候,流中數(shù)據(jù) key 不能重復(fù)
Map<String,Integer> map = list2.stream()
.filter(s->s.startsWith("陳"))
/**
* toMap:參數(shù)一表示鍵的生成規(guī)則
* 參數(shù)二表示值的生成規(guī)則
* 參數(shù)一:
* Function 泛型一:表示流中數(shù)據(jù)類型
* 泛型二:表示Map集合中key的數(shù)據(jù)類型
* 方法 apply 形參:依次表示流里面的每一個(gè)數(shù)據(jù)
* 返回值: Map 的 key
* 參數(shù)二:
* Function 泛型一:表示流中數(shù)據(jù)類型
* 泛型二:表示Map集合中value的數(shù)據(jù)類型
* 方法 apply 形參:依次表示流里面的每一個(gè)數(shù)據(jù)
* 返回值:Map 的 value
*/
.collect(Collectors.toMap(new Function<String, String>() {
@Override
public String apply(String s) {
return s.split("-")[0];
}
}, new Function<String, Integer>() {
@Override
public Integer apply(String s) {
return Integer.parseInt(s.split("-")[1]);
}
}));
System.out.println(map);
//使用 lambda 表達(dá)式
Map<String,Integer> map2 =
list2.stream()
.filter(s->s.startsWith("陳"))
.collect(Collectors.toMap(s->s.split("-")[0],s->Integer.parseInt(s.split("-")[1])));
System.out.println(map2);
}結(jié)果:

如果流中有重復(fù)的key,集合到Map時(shí)會(huì)報(bào)錯(cuò),這跟集合到List不一樣。
public static void main(String[] args) {
List<String> list2 = new ArrayList<>();
list2.add("陳大帥-20");
list2.add("陳大帥-21");
list2.add("陳小帥-12");
list2.add("小紅-15");
list2.add("李四-15");
list2.add("陳四-38");
//使用 lambda 表達(dá)式
Map<String,Integer> map2 =
list2.stream()
.filter(s->s.startsWith("陳"))
.collect(Collectors.toMap(s->s.split("-")[0],s->Integer.parseInt(s.split("-")[1])));
System.out.println(map2);
}結(jié)果:

以上就是詳解Java中Stream流的用法和原理的詳細(xì)內(nèi)容,更多關(guān)于Java Stream流的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
深入學(xué)習(xí)spring cloud gateway 限流熔斷
這篇文章主要介紹了深入學(xué)習(xí)spring cloud gateway 限流熔斷,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-04-04
使用mybatis 實(shí)現(xiàn)量表關(guān)聯(lián)并且統(tǒng)計(jì)數(shù)據(jù)量的步驟和代碼
本文介紹了使用SpringBoot+MyBatis+EasyExcel技術(shù)棧實(shí)現(xiàn)數(shù)據(jù)庫(kù)查詢結(jié)果導(dǎo)出為Excel文件的步驟,本文結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2025-10-10
SpringBoot整合JSP的實(shí)現(xiàn)示例
JSP是一種動(dòng)態(tài)網(wǎng)頁(yè)技術(shù),允許開(kāi)發(fā)者在 HTML中嵌入Java代碼,將 Spring Boot與JSP 合,可以創(chuàng)建具有傳統(tǒng)服務(wù)器端渲染能力的現(xiàn)代 Web 應(yīng)用,下面就來(lái)詳細(xì)的介紹一下,感興趣的可以了解一下2025-12-12
SpringBoot+Mybatis使用Enum枚舉類型總是報(bào)錯(cuò)No enum constant&n
這篇文章主要介紹了SpringBoot+Mybatis使用Enum枚舉類型總是報(bào)錯(cuò)No enum constant XX問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-12-12
JavaSwing FlowLayout 流式布局的實(shí)現(xiàn)
這篇文章主要介紹了JavaSwing FlowLayout 流式布局的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-12-12
Spring Boot 中的 CommandLineRunner 原理及使用示例
CommandLineRunner 是 Spring Boot 提供的一個(gè)非常有用的接口,可以幫助你在應(yīng)用程序啟動(dòng)后執(zhí)行初始化任務(wù),本文通過(guò)多個(gè)示例詳細(xì)介紹了如何在實(shí)際項(xiàng)目中使用 CommandLineRunner,感興趣的朋友一起看看吧2025-04-04
Java實(shí)現(xiàn)上傳網(wǎng)絡(luò)圖片到七牛云存儲(chǔ)詳解
這篇文章主要為大家詳細(xì)介紹了Java如何實(shí)現(xiàn)上傳網(wǎng)絡(luò)圖片到七牛云存儲(chǔ),文中的示例代碼講解詳細(xì),具有一定的借鑒價(jià)值,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2022-12-12

