作者:litesky
相信Java8的Stream 大家都已聽說過了,但是可能大家不會用或者用的不熟斥难,文章將帶大家從零開始使用障癌,循序漸進(jìn)娄周,帶你走向Stream的巔峰。
操作符
什么是操作符呢?操作符就是對數(shù)據(jù)進(jìn)行的一種處理工作诈闺,一道加工程序;就好像工廠的工人對流水線上的產(chǎn)品進(jìn)行一道加工程序一樣铃芦。
Stream的操作符大體上分為兩種:中間操作符和終止操作符
中間操作符
對于數(shù)據(jù)流來說雅镊,中間操作符在執(zhí)行制定處理程序后,數(shù)據(jù)流依然可以傳遞給下一級的操作符刃滓。
中間操作符包含8種(排除了parallel,sequential,這兩個操作并不涉及到對數(shù)據(jù)流的加工操作):
map(mapToInt,mapToLong,mapToDouble) 轉(zhuǎn)換操作符仁烹,把比如A->B,這里默認(rèn)提供了轉(zhuǎn)int咧虎,long卓缰,double的操作符。
flatmap(flatmapToInt,flatmapToLong,flatmapToDouble) 拍平操作比如把 int[]{2,3,4} 拍平 變成 2砰诵,3征唬,4 也就是從原來的一個數(shù)據(jù)變成了3個數(shù)據(jù),這里默認(rèn)提供了拍平成int,long,double的操作符茁彭。
limit 限流操作总寒,比如數(shù)據(jù)流中有10個 我只要出前3個就可以使用。
distint 去重操作理肺,對重復(fù)元素去重摄闸,底層使用了equals方法。
filter 過濾操作妹萨,把不想要的數(shù)據(jù)過濾年枕。
peek 挑出操作,如果想對數(shù)據(jù)進(jìn)行某些操作乎完,如:讀取熏兄、編輯修改等。
skip 跳過操作,跳過某些元素霍弹。
sorted(unordered) 排序操作毫别,對元素排序,前提是實(shí)現(xiàn)Comparable接口典格,當(dāng)然也可以自定義比較器岛宦。
終止操作符
數(shù)據(jù)經(jīng)過中間加工操作,就輪到終止操作符上場了耍缴;終止操作符就是用來對數(shù)據(jù)進(jìn)行收集或者消費(fèi)的砾肺,數(shù)據(jù)到了終止操作這里就不會向下流動了,終止操作符只能使用一次防嗡。
collect 收集操作变汪,將所有數(shù)據(jù)收集起來,這個操作非常重要蚁趁,官方的提供的Collectors 提供了非常多收集器裙盾,可以說Stream 的核心在于Collectors。
count 統(tǒng)計(jì)操作他嫡,統(tǒng)計(jì)最終的數(shù)據(jù)個數(shù)番官。
findFirst、findAny 查找操作钢属,查找第一個徘熔、查找任何一個 返回的類型為Optional。
noneMatch淆党、allMatch酷师、anyMatch 匹配操作,數(shù)據(jù)流中是否存在符合條件的元素 返回值為bool 值染乌。
min山孔、max 最值操作,需要自定義比較器慕匠,返回?cái)?shù)據(jù)流中最大最小的值饱须。
reduce 規(guī)約操作,將整個數(shù)據(jù)流的值規(guī)約為一個值台谊,count、min譬挚、max底層就是使用reduce锅铅。
forEach、forEachOrdered 遍歷操作减宣,這里就是對最終的數(shù)據(jù)進(jìn)行消費(fèi)了盐须。
toArray 數(shù)組操作,將數(shù)據(jù)流的元素轉(zhuǎn)換成數(shù)組漆腌。
這里只介紹了Stream贼邓,并沒有涉及到IntStream阶冈、LongStream、DoubleStream塑径,這三個流實(shí)現(xiàn)了一些特有的操作符女坑,我將在后續(xù)文章中介紹到。Java知音公眾號內(nèi)回復(fù)“面試題聚合”统舀,送你一份各大公司面試匯總寶典匆骗。
說了這么多,只介紹這些操作符還遠(yuǎn)遠(yuǎn)不夠誉简;俗話說碉就,實(shí)踐出真知。那么闷串,Let‘s go瓮钥。
代碼演練
Stream 的一系列操作必須要使用終止操作,否者整個數(shù)據(jù)流是不會流動起來的烹吵,即處理操作不會執(zhí)行骏庸。
map,可以看到 map 操作符要求輸入一個Function的函數(shù)是接口實(shí)例年叮,功能是將T類型轉(zhuǎn)換成R類型的具被。
map操作將原來的單詞 轉(zhuǎn)換成了每個單的長度,利用了String自身的length()方法只损,該方法返回類型為int一姿。這里我直接使用了lambda表達(dá)式,關(guān)于lambda表達(dá)式 還請讀者們自行了解吧跃惫。
public class Main {
public static void main(String[] args) {
Stream.of("apple","banana","orange","waltermaleon","grape")
.map(e->e.length()) //轉(zhuǎn)成單詞的長度 int
.forEach(e->System.out.println(e)); //輸出
}
}
當(dāng)然也可以這樣叮叹,這里使用了成員函數(shù)引用,為了便于讀者們理解爆存,后續(xù)的例子中將使用lambda表達(dá)式而非函數(shù)引用蛉顽。
public class Main {
public static void main(String[] args) {
Stream.of("apple","banana","orange","waltermaleon","grape")
.map(String::length) //轉(zhuǎn)成單詞的長度 int
.forEach(System.out::println);
}
}
結(jié)果如圖:
mapToInt 將數(shù)據(jù)流中得元素轉(zhuǎn)成Int,這限定了轉(zhuǎn)換的類型Int先较,最終產(chǎn)生的流為IntStream携冤,及結(jié)果只能轉(zhuǎn)化成int。
public class Main {
public static void main(String[] args) {
Stream.of("apple", "banana", "orange", "waltermaleon", "grape")
.mapToInt(e -> e.length()) //轉(zhuǎn)成int
.forEach(e -> System.out.println(e));
}
}
mapToInt如圖:
mapToLong闲勺、mapToDouble 與mapToInt 類似
public class Main {
public static void main(String[] args) {
Stream.of("apple", "banana", "orange", "waltermaleon", "grape")
.mapToLong(e -> e.length()) //轉(zhuǎn)成long ,本質(zhì)上是int 但是存在類型自動轉(zhuǎn)換
.forEach(e -> System.out.println(e));
}
}
mapToLong 如圖:
public class Main {
public static void main(String[] args) {
Stream.of("apple", "banana", "orange", "waltermaleon", "grape")
.mapToDouble(e -> e.length()) //轉(zhuǎn)成Double 曾棕,自動類型轉(zhuǎn)換成Double
.forEach(e -> System.out.println(e));
}
}
mapToDouble如圖:
flatmap 作用就是將元素拍平拍扁 ,將拍扁的元素重新組成Stream菜循,并將這些Stream 串行合并成一條Stream
public class Main {
public static void main(String[] args) {
Stream.of("a-b-c-d","e-f-i-g-h")
.flatMap(e->Stream.of(e.split("-")))
.forEach(e->System.out.println(e));
}
}
flatmap 如圖:
flatmapToInt翘地、flatmapToLong、flatmapToDouble 跟flatMap 都類似的,只是類型被限定了衙耕,這里就不在舉例子了昧穿。
limit 限制元素的個數(shù),只需傳入 long 類型 表示限制的最大數(shù)
public class Main {
public static void main(String[] args) {
Stream.of(1,2,3,4,5,6)
.limit(3) //限制三個
.forEach(e->System.out.println(e)); //將輸出 前三個 1橙喘,2时鸵,3
}
}
limit如圖:
public class Main {
public static void main(String[] args) {
Stream.of(1,2,3,1,2,5,6,7,8,0,0,1,2,3,1)
.distinct() //去重
.forEach(e->System.out.println(e));
}
}
distinct 如圖:
filter 對某些元素進(jìn)行過濾,不符合篩選條件的將無法進(jìn)入流的下游
public class Main {
public static void main(String[] args) {
Stream.of(1,2,3,1,2,5,6,7,8,0,0,1,2,3,1)
.filter(e->e>=5) //過濾小于5的
.forEach(e->System.out.println(e));
}
}
filter 如圖:
peek 挑選 渴杆,將元素挑選出來寥枝,可以理解為提前消費(fèi)
public class Main {
public static void main(String[] args) {
User w = new User("w",10);
User x = new User("x",11);
User y = new User("y",12);
Stream.of(w,x,y)
.peek(e->{e.setName(e.getAge()+e.getName());}) //重新設(shè)置名字 變成 年齡+名字
.forEach(e->System.out.println(e.toString()));
}
static class User {
private String name;
private int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
}
peek 如圖:
skip 跳過 元素
public class Main {
public static void main(String[] args) {
Stream.of(1,2,3,4,5,6,7,8,9)
.skip(4) //跳過前四個
.forEach(e->System.out.println(e)); //輸出的結(jié)果應(yīng)該只有5,6磁奖,7囊拜,8,9
}
}
skip 如圖:
sorted 排序 底層依賴Comparable 實(shí)現(xiàn)比搭,也可以提供自定義比較器
這里Integer 實(shí)現(xiàn)了比較器
public class Main {
public static void main(String[] args) {
Stream.of(2,1,3,6,4,9,6,8,0)
.sorted()
.forEach(e->System.out.println(e));
}
}
sorted 默認(rèn)比較器如圖:
這里使用自定義比較冠跷,當(dāng)然User 可以實(shí)現(xiàn)Comparable 接口
public class Main {
public static void main(String[] args) {
User x = new User("x",11);
User y = new User("y",12);
User w = new User("w",10);
Stream.of(w,x,y)
.sorted((e1,e2)->e1.age>e2.age?1:e1.age==e2.age?0:-1)
.forEach(e->System.out.println(e.toString()));
}
static class User {
private String name;
private int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
}
如圖:
collect 收集,使用系統(tǒng)提供的收集器可以將最終的數(shù)據(jù)流收集到List身诺,Set蜜托,Map等容器中。
這里我使用collect 將元素收集到一個set中
public class Main {
public static void main(String[] args) {
Stream.of("apple", "banana", "orange", "waltermaleon", "grape")
.collect(Collectors.toSet()) //set 容器
.forEach(e -> System.out.println(e));
}
}
咦霉赡?橄务,不是說終止操作符只能使用一次嗎,為什么這里調(diào)用了forEach 呢穴亏?forEach不僅僅是是Stream 中得操作符還是各種集合中得一個語法糖蜂挪,不信咋們試試。Java知音公眾號內(nèi)回復(fù)“面試題聚合”嗓化,送你一份各大公司面試匯總寶典棠涮。
public class Main {
public static void main(String[] args) {
Set<String> stringSet = Stream.of("apple", "banana", "orange", "waltermaleon", "grape")
.collect(Collectors.toSet()); //收集的結(jié)果就是set
stringSet.forEach(e->System.out.println(e)); set的語法糖forEach
}
結(jié)果如圖:
count 統(tǒng)計(jì)數(shù)據(jù)流中的元素個數(shù),返回的是long 類型
public class Main {
public static void main(String[] args) {
long count = Stream.of("apple", "banana", "orange", "waltermaleon", "grape")
.count();
System.out.println(count);
}
}
count 如圖:
findFirst 獲取流中的第一個元素
這里找到第一個元素 apple
public class FindFirst {
public static void main(String[] args) {
Optional<String> stringOptional = Stream.of("apple", "banana", "orange", "waltermaleon", "grape")
.findFirst();
stringOptional.ifPresent(e->System.out.println(e));
}
}
findFirst 結(jié)果如圖:
findAny 獲取流中任意一個元素
public class FindAny {
public static void main(String[] args) {
Optional<String> stringOptional = Stream.of("apple", "banana", "orange", "waltermaleon", "grape")
.parallel()
.findAny(); //在并行流下每次返回的結(jié)果可能一樣也可能不一樣
stringOptional.ifPresent(e->System.out.println(e));
}
}
findAny 在并行流下 使用結(jié)果:
輸出了orange
輸出了banana
noneMatch 數(shù)據(jù)流中得沒有一個元素與條件匹配的
這里 的作用是是判斷數(shù)據(jù)流中 一個都沒有與aa 相等元素 刺覆,但是流中存在 aa 严肪,所以最終結(jié)果應(yīng)該是false
public class NoneMatch {
public static void main(String[] args) {
boolean result = Stream.of("aa","bb","cc","aa")
.noneMatch(e->e.equals("aa"));
System.out.println(result);
}
}
noneMatch 如圖:
allMatch和anyMatch 一個是全匹配,一個是任意匹配 和noneMatch 類似谦屑,這里就不在舉例了驳糯。
min 最小的一個,傳入比較器伦仍,也可能沒有(如果數(shù)據(jù)流為空)
public class Main {
public static void main(String[] args) {
Optional<Integer> integerOptional = Stream.of(0,9,8,4,5,6,-1)
.min((e1,e2)->e1.compareTo(e2));
integerOptional.ifPresent(e->System.out.println(e));
}
min如圖:
max 元素中最大的结窘,需要傳入比較器,也可能沒有(流為Empty時)
public class Main {
public static void main(String[] args) {
Optional<Integer> integerOptional = Stream.of(0,9,8,4,5,6,-1)
.max((e1,e2)->e1.compareTo(e2));
integerOptional.ifPresent(e->System.out.println(e));
}
}
max 如圖:
reduce 是一個規(guī)約操作充蓝,所有的元素歸約成一個,比如對所有元素求和,乘啊等谓苟。
這里實(shí)現(xiàn)了一個加法官脓,指定了初始化的值
public class Main {
public static void main(String[] args) {
int sum = Stream.of(0,9,8,4,5,6,-1)
.reduce(0,(e1,e2)->e1+e2);
System.out.println(sum);
}
}
reduce 如圖:
forEach
forEach 其實(shí)前就已經(jīng)見過了,對每個數(shù)據(jù)遍歷迭代
forEachOrdered 適用用于并行流的情況下進(jìn)行迭代涝焙,能保證迭代的有序性
這里通過并行的方式輸出數(shù)字
public class ForEachOrdered {
public static void main(String[] args) {
Stream.of(0,2,6,5,4,9,8,-1)
.parallel()
.forEachOrdered(e->{
System.out.println(Thread.currentThread().getName()+": "+e);});
}
}
forEachOrdered 如圖:
toArray 轉(zhuǎn)成數(shù)組卑笨,可以提供自定義數(shù)組生成器
public class ToArray {
public static void main(String[] args) {
Object[] objects=Stream.of(0,2,6,5,4,9,8,-1)
.toArray();
for (int i = 0; i < objects.length; i++) {
System.out.println(objects[I]);
}
}
}
toArray 如圖:
總結(jié)
Java8 Stream就帶大家認(rèn)識到這里,如果你能跟著我的文章把每一個例子都敲一遍仑撞,相信都能掌握這些操作符的初步用法赤兴。