詳解Java中Stream流的用法和原理
一、創(chuàng)建不可變的集合
不可變的集合,顧名思義,就是不想讓別人修改集合中的內(nèi)容,也無法修改。如何創(chuàng)建呢?
(一)創(chuàng)建格式
在List、Set、Map接口中,都存在靜態(tài)的of方法,可以獲取一個不可變的集合:
- static List of(E...elements) 創(chuàng)建一個具有指定元素的List集合對象。
- static Set of(E...elements) 創(chuàng)建一個具有指定元素的Set集合對象。
- static <K , V> Map<K,V> of(E...elements) 創(chuàng)建一個具有指定元素的Map集合對象。
(List.of在jdk8中沒有,需要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("------------------------------"); }
- 這些集合不能添加,不能刪除,不能修改,如果修改會拋異常:
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 個鍵值對,源碼里of()方法參數(shù)最多的也就10對。
那為什么不用可變參數(shù)呢?因為可變參數(shù)只能有一個,并且要在參數(shù)列表的末尾。如果想要存超過10個的鍵值對,我們可以利用數(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"); //獲取所有的鍵值對 Set<Map.Entry<Integer,String>> entries = map.entrySet(); //把entries變成一個數(shù)組 //如果集合的長度 〉數(shù)組的長度﹔數(shù)據(jù)在數(shù)組中放不下,此時會根據(jù)實際數(shù)據(jù)的個數(shù),重新創(chuàng)建數(shù)組 //如果集合的長度〈=數(shù)組的長度:數(shù)據(jù)在數(shù)組中放的下,此時不會創(chuàng)建新的數(shù)組,而是直接用原來的數(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)建之后就不再變更的對象,這種特性使得它們天生支持線程安全。
- 防止數(shù)據(jù)被篡改:不可變集合可以用于封裝一些敏感或重要的數(shù)據(jù),防止它們被外部程序或不可信的庫修改或破壞。
二、Stream 流
(一)Stream 的思想
Stream 為什么叫流?可以通過下面的案例來引入這個概念。
public static void main(String[] args) { List<String> list = new ArrayList<>(); list.add("陳大帥"); list.add("陳小帥"); list.add("小紅"); list.add("李四"); list.add("陳四"); //篩選姓陳的,并且名字是3個字的人 list.stream().filter(name->name.startsWith("陳")) .filter(name->name.length()==3) .forEach(name-> System.out.println(name)); }
Stream 就像流水線一樣,這個流水線第一道工序就是過濾掉不姓陳的,第二道工序過濾掉字的數(shù)量不等于3的,最后的工序就是打印。
(二)Stream流的使用步驟
- 得到一條Stream流水線,把數(shù)據(jù)放上去。
- 利用Stream流中的API進(jìn)行各種操作。
中間方法:方法調(diào)用后還可以調(diào)用其它方法,如上面的filter。
終結(jié)方法:最后一步,調(diào)用后不能調(diào)用其它方法。
1.把數(shù)據(jù)放到流水線上
獲取方式 | 方法名 | 說明 |
---|---|---|
單列集合 | default Stream stream() | collection中的默認(rèn)方法(由于單列集合實現(xiàn)了Collection接口,可以直接.stream()) |
雙列集合(Map等) | 無 | 無法直接使用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)系來操作整體 map.entrySet().stream().forEach(entry-> System.out.println(entry));//方式2:獲取鍵值對的流水線 //3.數(shù)組 int[] arr = {1,2,3,4,5,6,7,8}; Arrays.stream(arr).forEach(x-> System.out.println(x));//利用 Arrays 工具類來獲取流 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 來獲取 }
2.中間方法
Stream方法 | 中文解釋 | 作用 |
---|---|---|
Stream filter(Predicate<? super T> predicate) | 篩選 | 過濾出符合條件的元素 |
Stream limit(long maxSize) | 限制 | 截取前maxSize個元素 |
Stream skip(long n) | 跳過 | 跳過前n個元素 |
Stream distinct() | 去重 | 去除重復(fù)的元素(根據(jù)hashCode和equals方法) |
static Stream concat(Stream a, Stream b) | 合并 | 將兩個Stream合并為一個 |
Stream map(Function<T, R> mapper) | 映射 | 將每個元素轉(zhuǎn)換為另一種類型或形式 |
注意1:每個中間方法都是返回新的Stream流,原來的Stream流只能使用一次,建議使用鏈?zhǔn)骄幊獭?/p>
注意2:修改Stream流中的數(shù)據(jù),不會影響原來集合或者數(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個字的人 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));//通過匿名內(nèi)部類 list.stream().filter(s->s.startsWith("陳")).forEach(s -> System.out.println(s));//通過 lambda }
- limit
public static void main(String[] args) { List<String> list = new ArrayList<>(); list.add("陳大帥"); list.add("陳小帥"); list.add("小紅"); list.add("李四"); list.add("陳四"); //截取前 3 個數(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("陳四"); //跳過前 3 個數(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"); //合并,如果類型不相同,類型就會變?yōu)楣餐母割? Stream.concat(list.stream(),list2.stream()).forEach(s -> System.out.println(s)); }
- 將每個元素轉(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>() { //第一個類型:流中原本的數(shù)據(jù)類型 // 第二個類型:要轉(zhuǎn)成之后的類型 @Override public Integer apply(String s) { //apply的形參s:依次表示流里面的每一個數(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) | 遍歷 | 對每個元素執(zhí)行指定的操作 |
long count() | 統(tǒng)計 | 返回元素的個數(shù) |
toArray() | 收集到數(shù)組 | 返回一個包含所有元素的數(shù)組 |
collect(Collector collector) | 收集到集合 | 返回一個包含所有元素的集合,可以指定集合的類型和特性 |
- 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:依次表示流里面的每一個數(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)計集合中的個數(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)); //方式二:指定對于類型 //IntFunction的泛型:具體類型的數(shù)組 //toArray方法的參數(shù):負(fù)責(zé)創(chuàng)建一個指定類型的數(shù)組 //toArray方法的底層:會依次得到流里面的每一個數(shù)據(jù),并把數(shù)據(jù)放到數(shù)組當(dāng)中 String[] strings = list.stream().toArray(new IntFunction<String[]>() { /** * * @param value 流中數(shù)據(jù)的個數(shù),要跟數(shù)組長度保持一致 * @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中,自動去重 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ù)據(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 形參:依次表示流里面的每一個數(shù)據(jù) * 返回值: Map 的 key * 參數(shù)二: * Function 泛型一:表示流中數(shù)據(jù)類型 * 泛型二:表示Map集合中value的數(shù)據(jù)類型 * 方法 apply 形參:依次表示流里面的每一個數(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時會報錯,這跟集合到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流的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Java數(shù)據(jù)結(jié)構(gòu)學(xué)習(xí)之樹
這篇文章主要介紹了Java數(shù)據(jù)結(jié)構(gòu)學(xué)習(xí)之樹,文中有非常詳細(xì)的代碼示例,對正在學(xué)習(xí)java數(shù)據(jù)結(jié)構(gòu)的小伙伴們有非常好的幫助,需要的朋友可以參考下2021-05-05java向上轉(zhuǎn)型發(fā)生的時機(jī)知識點(diǎn)詳解
在本篇文章里小編給大家整理分享的是關(guān)于java向上轉(zhuǎn)型發(fā)生的時機(jī)知識點(diǎn)內(nèi)容,有興趣的讀者們可以參考下。2021-05-05springboot項目同時啟動web服務(wù)和grpc服務(wù)的方法
本文主要介紹了springboot項目同時啟動web服務(wù)和grpc服務(wù)的方法,通過實際代碼示例展示了實現(xiàn),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2024-02-02Maven在Windows中的配置以及IDE中的項目創(chuàng)建(圖文教程)
這篇文章主要介紹了Maven在Windows中的配置以及IDE中的項目創(chuàng)建(圖文教程),具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-09-09SpringCloud-Alibaba-Sentinel-配置持久化策略詳解
這篇文章主要介紹了SpringCloud-Alibaba-Sentinel-配置持久化策略,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-03-03解決Idea報錯There is not enough memory
在使用Idea開發(fā)過程中,可能會遇到因內(nèi)存不足導(dǎo)致的閃退問題,出現(xiàn)"There is not enough memory to perform the requested operation"錯誤時,可以通過調(diào)整Idea的虛擬機(jī)選項來解決,方法是在Idea的Help菜單中選擇Edit Custom VM Options2024-11-11