位元組二面被問Java Stream 流操作‘’?看完這篇,教你自信應對
Stream
將要處理的元素集合看作一種流,在流的過程中,藉助
Stream API
對流中的元素進行操作,比如:篩選、排序、聚合等。
image-20210701194245361
Stream
的運算子大體上分為兩種:
中間運算子
和
終止運算子
中間運算子
對於資料流來說,中間運算子在執行指定處理程式後,資料流依然可以傳遞給下一級的運算子。
中間運算子包含8種(排除了parallel,sequential,這兩個操作並不涉及到對資料流的加工操作):
map(mapToInt,mapToLong,mapToDouble) 轉換運算子,把比如A->B,這裡預設提供了轉int,long,double的運算子。
flatmap(flatmapToInt,flatmapToLong,flatmapToDouble) 拍平操作比如把 int[]{2,3,4} 拍平 變成 2,3,4 也就是從原來的一個數據變成了3個數據,這裡預設提供了拍平成int,long,double的運算子。
limit 限流操作,比如資料流中有10個 我只要出前3個就可以使用。
distint 去重操作,對重複元素去重,底層使用了equals方法。
filter 過濾操作,把不想要的資料過濾。
peek 挑出操作,如果想對資料進行某些操作,如:讀取、編輯修改等。
skip 跳過操作,跳過某些元素。
sorted(unordered) 排序操作,對元素排序,前提是實現Comparable介面,當然也可以自定義比較器。
終止運算子
資料經過中間加工操作,就輪到終止運算子上場了;
終止運算子就是用來對資料進行收集或者消費的,資料到了終止操作這裡就不會向下流動了,終止運算子只能使用一次。
collect 收集操作,將所有資料收集起來,這個操作非常重要,官方的提供的Collectors 提供了非常多收集器,可以說Stream 的核心在於Collectors。
count 統計操作,統計最終的資料個數。
findFirst、findAny 查詢操作,查詢第一個、查詢任何一個 返回的型別為Optional。
noneMatch、allMatch、anyMatch 匹配操作,資料流中是否存在符合條件的元素 返回值為bool 值。
min、max 最值操作,需要自定義比較器,返回資料流中最大最小的值。
reduce 規約操作,將整個資料流的值規約為一個值,count、min、max底層就是使用reduce。
forEach、forEachOrdered 遍歷操作,這裡就是對最終的資料進行消費了。
toArray 陣列操作,將資料流的元素轉換成陣列。
Stream的建立
1、透過
java。util。Collection。stream()
方法用集合建立流
List
2、使用
java。util。Arrays。stream(T[] array)
方法用陣列建立流
int[] array={1,3,5,6,8};IntStream stream = Arrays。stream(array);
3、使用
Stream
的靜態方法:
of()、iterate()、generate()
Stream
輸出結果:
3690。81066234426861140。115546437273884580。1404645961428974Process finished with exit code 0
stream
和
parallelStream
的簡單區分:
stream
是順序流,由主執行緒按順序對流執行操作;
parallelStream
是並行流,內部以多執行緒並行執行的方式對流進行操作,但前提是流中的資料處理沒有順序要求。
例如篩選集合中的奇數,兩者的處理不同之處:
image-20210701230623951
Stream使用
遍歷/匹配(foreach/find/match)
Stream
也是支援類似集合的遍歷和匹配元素的,只是
Stream
中的元素是以
Optional
型別存在的。
Stream
的遍歷、匹配非常簡單。
public class StreamTest { public static void main(String[] args) { List
輸出結果:
798匹配第一個值:7匹配任意一個值:8是否存在大於6的值:trueProcess finished with exit code 0
篩選(filter)
篩選,是按照一定的規則校驗流中的元素,將符合條件的元素提取到新的流中的操作。
篩選出Integer集合中大於7的元素,並打印出來
public class StreamTest { public static void main(String[] args) { List
輸出結果:
89Process finished with exit code 0
聚合(max/min/count)
max
、
min
、
count
這些字眼你一定不陌生,沒錯,在mysql中我們常用它們進行資料統計。Java stream中也引入了這些概念和用法,極大地方便了我們對集合、陣列的資料統計工作。
案例一:獲取String集合中最長的元素。
public class StreamTest { public static void main(String[] args) { List
輸出結果:
最長的字串:weoujgsdProcess finished with exit code 0
案例二:獲取Integer集合中的最大值。
public class StreamTest { public static void main(String[] args) { List
輸出結果:
自然排序的最大值:11自定義排序的最大值:11Process finished with exit code 0
案例三:計算Integer集合中大於6的元素的個數。
public class StreamTest { public static void main(String[] args) { List
輸出結果:
list中大於6的元素個數:4Process finished with exit code 0
對映(map/flatMap)
對映,可以將一個流的元素按照一定的對映規則對映到另一個流中。分為
map
和
flatMap
:
map
:接收一個函式作為引數,該函式會被應用到每個元素上,並將其對映成一個新的元素。
flatMap
:接收一個函式作為引數,將流中的每個值都換成另一個流,然後把所有流連線成一個流。
案例一:英文字串陣列的元素全部改為大寫。整數陣列每個元素+3。
public class StreamTest { public static void main(String[] args) { String[] strArr = { “abcd”, “bcdd”, “defde”, “fTr” }; List
輸出結果:
每個元素大寫:[ABCD, BCDD, DEFDE, FTR]每個元素+3:[4, 6, 8, 10, 12, 14]Process finished with exit code 0
案例二:將兩個字元數組合併成一個新的字元陣列。
public class StreamTest { public static void main(String[] args) { List
輸出結果:
處理前的集合:[m,k,l,a, 1,3,5,7]處理後的集合:[m, k, l, a, 1, 3, 5, 7]Process finished with exit code 0
歸約(reduce)
歸約,也稱縮減,顧名思義,是把一個流縮減成一個值,能實現對集合求和、求乘積和求最值操作。
案例一:求Integer集合的元素之和、乘積和最大值。
public class StreamTest { public static void main(String[] args) { List
輸出結果:
list求和:29,29,29list求積:2112list求和:11,11Process finished with exit code 0
歸集(toList/toSet/toMap)
因為流不儲存資料,那麼在流中的資料完成處理後,需要將流中的資料重新歸集到新的集合裡。
toList
、
toSet
和
toMap
比較常用,另外還有
toCollection
、
toConcurrentMap
等複雜一些的用法。
下面用一個案例演示
toList
、
toSet
和
toMap
:
public class Person { private String name; // 姓名 private int salary; // 薪資 private int age; // 年齡 private String sex; //性別 private String area; // 地區 // 構造方法 public Person(String name, int salary, int age,String sex,String area) { this。name = name; this。salary = salary; this。age = age; this。sex = sex; this。area = area; } public String getName() { return name; } public void setName(String name) { this。name = name; } public int getSalary() { return salary; } public void setSalary(int salary) { this。salary = salary; } public int getAge() { return age; } public void setAge(int age) { this。age = age; } public String getSex() { return sex; } public void setSex(String sex) { this。sex = sex; } public String getArea() { return area; } public void setArea(String area) { this。area = area; } @Override public String toString() { return “Person{” + “name=‘” + name + ’\‘’ + “, salary=” + salary + “, age=” + age + “, sex=‘” + sex + ’\‘’ + “, area=‘” + area + ’\‘’ + ‘}’; }}
public class StreamTest { public static void main(String[] args) { List
輸出結果:
toList:[6, 4, 6, 6, 20]toSet:[4, 20, 6]toMap:{Tom=Person{name=‘Tom’, salary=8900, age=23, sex=‘male’, area=‘New York’}, Anni=Person{name=‘Anni’, salary=8200, age=24, sex=‘female’, area=‘New York’}}Process finished with exit code 0
統計(count/averaging)
Collectors
提供了一系列用於資料統計的靜態方法:
計數:
count
平均值:
averagingInt
、
averagingLong
、
averagingDouble
最值:
maxBy
、
minBy
求和:
summingInt
、
summingLong
、
summingDouble
統計以上所有:
summarizingInt
、
summarizingLong
、
summarizingDouble
案例:統計員工人數、平均工資、工資總額、最高工資。
public class StreamTest { public static void main(String[] args) { List
輸出結果:
員工總數:3員工平均工資:7900。0員工最高工資:8900員工工資總和:23700員工工資所有統計:DoubleSummaryStatistics{count=3, sum=23700。000000, min=7000。000000, average=7900。000000, max=8900。000000}Process finished with exit code 0
分組(partitioningBy/groupingBy)
分割槽:將
stream
按條件分為兩個
Map
,比如員工按薪資是否高於8000分為兩部分。
分組:將集合分為多個Map,比如員工按性別分組。有單級分組和多級分組。
案例:將員工按薪資是否高於8000分為兩部分;將員工按性別和地區分組
public class StreamTest { public static void main(String[] args) { List
輸出結果:
員工按薪資是否大於8000分組情況:{false=[Person{name=‘Jack’, salary=7000, age=25, sex=‘male’, area=‘Washington’}, Person{name=‘Lily’, salary=7800, age=21, sex=‘female’, area=‘New York’}], true=[Person{name=‘Tom’, salary=8900, age=23, sex=‘male’, area=‘Washington’}, Person{name=‘Anni’, salary=8200, age=24, sex=‘female’, area=‘New York’}]}員工按性別分組情況:{female=[Person{name=‘Lily’, salary=7800, age=21, sex=‘female’, area=‘New York’}, Person{name=‘Anni’, salary=8200, age=24, sex=‘female’, area=‘New York’}], male=[Person{name=‘Tom’, salary=8900, age=23, sex=‘male’, area=‘Washington’}, Person{name=‘Jack’, salary=7000, age=25, sex=‘male’, area=‘Washington’}]}員工按性別、地區:{female={New York=[Person{name=‘Lily’, salary=7800, age=21, sex=‘female’, area=‘New York’}, Person{name=‘Anni’, salary=8200, age=24, sex=‘female’, area=‘New York’}]}, male={Washington=[Person{name=‘Tom’, salary=8900, age=23, sex=‘male’, area=‘Washington’}, Person{name=‘Jack’, salary=7000, age=25, sex=‘male’, area=‘Washington’}]}}Process finished with exit code 0
接合(joining)
joining
可以將stream中的元素用特定的連線符(沒有的話,則直接連線)連線成一個字串。
public class StreamTest { public static void main(String[] args) { List
輸出結果:
所有員工的姓名:Tom,Jack,Lily拼接後的字串:A-B-CProcess finished with exit code 0
排序(sorted)
sorted
,中間操作。有兩種排序:
sorted()
:自然排序,流中元素需實現
Comparable
介面
sorted(Comparator com)
:
Comparator
排序器自定義排序
案例:將員工按工資由高到低(工資一樣則按年齡由大到小)排序
public class StreamTest { public static void main(String[] args) { List
輸出結果:
按工資升序排序:[Lily, Tom, Sherry, Jack, Alisa]按工資降序排序:[Sherry, Jack, Alisa, Tom, Lily]先按工資再按年齡升序排序:[Lily, Tom, Sherry, Jack, Alisa]先按工資再按年齡自定義降序排序:[Alisa, Jack, Sherry, Tom, Lily]Process finished with exit code 0
提取/組合
流也可以進行合併、去重、限制、跳過等操作。
public class StreamTest { public static void main(String[] args) { String[] arr1 = { “a”, “b”, “c”, “d” }; String[] arr2 = { “d”, “e”, “f”, “g” }; Stream
輸出結果:
流合併:[a, b, c, d, e, f, g]limit:[1, 3, 5, 7, 9, 11, 13, 15, 17, 19]skip:[3, 5, 7, 9, 11]Process finished with exit code 0
分頁操作
stream api 的強大之處還不僅僅是對集合進行各種組合操作,還支援分頁操作。
例如,將如下的陣列從小到大進行排序,排序完成之後,從第1行開始,查詢10條資料出來,操作如下:
//需要查詢的資料List
輸出結果:
[2, 2, 3, 3, 3, 5, 6, 7, 10, 20]Process finished with exit code 0
並行操作
所謂並行,指的是多個任務在同一時間點發生,並由不同的cpu進行處理,不互相搶佔資源;而併發,指的是多個任務在同一時間點內同時發生了,但由同一個cpu進行處理,互相搶佔資源。
stream api 的並行操作和序列操作,只有一個方法區別,其他都一樣,例如下面我們使用parallelStream來輸出空字串的數量:
List
在實際使用的時候,並行操作不一定比序列操作快!對於簡單操作,數量非常大,同時伺服器是多核的話,建議使用Stream並行!反之,採用序列操作更可靠!
集合轉Map操作
在實際的開發過程中,還有一個使用最頻繁的操作就是,將集合元素中某個主鍵欄位作為key,元素作為value,來實現集合轉map的需求,這種需求在資料組裝方面使用的非常多。
public static void main(String[] args) { List
輸出結果:
{40=Person{name=‘Lucy’, salary=9000, age=40, sex=‘male’, area=‘上海’}, 25=Person{name=‘Tom’, salary=7000, age=25, sex=‘male’, area=‘安徽’}, 30=Person{name=‘Jack’, salary=8000, age=30, sex=‘female’, area=‘北京’}}Process finished with exit code 0
開啟
Collectors。toMap
方法原始碼,一起來看看。
public static
從引數表可以看出:
第一個引數:表示 key
第二個引數:表示 value
第三個引數:表示某種規則
上文中的
Collectors。toMap(Person::getAge, v -> v, (k1,k2) -> k1)
,表達的意思就是將
age
的內容作為
key
,
v -> v
是表示將元素
person
作為
value
,其中
(k1,k2) -> k1
表示如果存在相同的
key
,將第一個匹配的元素作為內容,第二個捨棄!
標籤: [Java]