內容來自《 java8實戰 》,本篇文章內容均爲非盈利,旨爲方便自己查詢、總結備份、開源分享。如有侵權請告知,馬上刪除。
書籍購買地址:java8實戰
-
篩選和切片
- filter方法
//取出是素菜的菜單 menu.stream() .filter(Dish::isVegetarian) //代表是否是素食,這裏條件並不一定是方法調用,只要的你條件能夠返回boolean就可以 .collect(Collectors.toList());
- limit方法
//取出菜單的前三個 menu.stream().limit(3) //跟sql中limit一樣,不過這個是短路limit,也就是說,它取出了三個後就不再遍歷後面的元素了,直接返回 .collect(Collectors.toList());
- distinct方法:去重
menu.stream() .distinct() //去重操作,根據元素的hashCode和equals方法判斷是否相等 .collect(Collectors.toList());
- skip方法:跳過指定數量的元素
//取菜單中第三個到第五個 menu.stream() .skip(2) //跳過開頭兩個元素 .limit(3) //然後截取第三個到第五個 .forEach(System.out::println); //如上可以看出skip和limit是互補的,因爲limit沒有這樣的形式:limit(start,end)
-
映射
- 提到這個概念,就是把你stream裏的每個元素都應用到自己指定的表達式之上然後返回,就比如
List<String> list = Arrays.asList("1","2","3"); list.stream() .映射方法(str -> str + "dada") .forEach(System.out::println); //以上爲僞代碼 //說明的意思就是list中的每個元素:1,2,3都會應用到表達式:str + "dada" //並返回表達式的值 //所以結果就是1dada,2dada,3dada
- map方法
//這裏提到的map方法,完全可以替換上面的僞代碼:映射方法,上面代碼是傳入A類型返回A類型的演示 //也就是傳入字符串1,2,3返回的也是字符串 //map當然可以傳入A類型返回B類型,比如 List<String> list = Arrays.asList("1","2","3"); list.stream() .map(Integer::parseInt) .forEach(System.out::println); //上面就是傳入String,返回的是int //當然也可以是返回字符串的長度
-
考慮一個場景,把n個單詞的裏面的字母去重然後輸出
- 就比如["abc","bce"],裏面的字母就是["a","b","c","b","c","e"],去重後是:["a","b","c","e"],現在來實現它
List<String> list = Arrays.asList("abc","bce"); List<String[]> collect = list.stream() .map(str -> str.split("")) .distinct() .collect(Collectors.toList()); collect.forEach(System.out::println);
-
- 上面的感覺是對的,拆分,然後去重,但是一點需要注意,當lambda把第一個元素"abc"進行映射操作的時候,split方法返回的是一個String數組對象,這也就能解釋爲什麼List的泛型是String數組了,數組跟數組distinct那肯定是去重不成功的。
- 流的過程是這樣的["abc","bce"] -> [{"a","b","c"},{"b","c","e"}],然後拿{"a","b","c"}和{"b","c","e"}去重肯定行不通
- 現在遇到的問題就是:我們希望把abc和bce切分然後合併成一個流,然後進行去重,我們接着實驗
-
Array::stream
- Array::stream可以接受一個數組併產生一個流,如下
String[] str = {"abc","bce"}; Stream<String> stream = Arrays.stream(str); stream.forEach(System.out::println); //abc //bce
- 我們在上面的split方法返回的就是數組,我們嘗試將split返回的數組映射到此方法上,看看能不能合併成一條流
List<String> list = Arrays.asList("abc","bce"); List<Stream<String>> collect = list.stream() .map(str -> str.split("")) .map(Arrays::stream) .distinct() .collect(Collectors.toList()); //java.util.stream.ReferencePipeline$Head@2aae9190 //java.util.stream.ReferencePipeline$Head@2f333739
- 但是並不盡如人意,因爲他返回的泛型是Stream,也就是說,Array::stream方法只是把一個數組轉換爲一個流,流中的元素是String所以這個流就是這樣的Stream,這種情況類似上面遇到的返回的是兩個數組,現在返回的List中裝入的並不是String,而是兩條流,流跟流做去重也是行不通的
-
解決方案
- 現在遇到的問題就是:他們都是將一個list元素單獨轉化爲一條流或者一個數組,我們需要的是轉換爲流或者數組之後,再將這些返回的數組或者流接爲一個流或者數組,就像接水管一樣,讓他們連起來
- 嘗試方法:flatmap
List<String> list = Arrays.asList("abc","bce"); List<String> collect = list.stream() .map(str -> str.split("")) .flatMap(Arrays::stream) .distinct() .collect(Collectors.toList()); collect.forEach(System.out::println); //a b c e
-
如上的代碼終於是返回了一個LIst,所以結果也是我們期待的達到了去重的效果,那麼它的流的處理過程是怎麼樣的呢?如下
-
傳入"abc"
->切分爲["a","b","c"]
-
--->flatMap合併["a","b","c","b","c","e"]
->去重
->ok
-
傳入"bce"
->切分爲["b","c","e"]
- 上面能看懂嗎...懶得畫圖了,第一行和第三行是flatMap前面的split過程,然後是第二行存入flatMap進行合併流
-
- flatMap也就是把n個流合併爲一個流
-
小實戰
- 要求兩個list:["123"],["456"],然後組成元組:(1,4),(1,5),(1,6),(2,4),(2,5)...
List<String> first = Arrays.asList("123"); List<String> second = Arrays.asList("456"); first.stream() .map(str -> str.split("")) .forEach(strs -> { Arrays.stream(strs).forEach(f -> { second.stream() .map(sec -> sec.split("")) .forEach(secs -> Arrays.stream(secs).forEach(s -> System.out.println("("+f+","+s+")"))); }); }); //上面代碼是直接輸出的,那麼下面的就是將元組組合成一個LIst返回了 List<String> first = Arrays.asList("123"); List<String> second = Arrays.asList("456"); List<String> collect = first.stream() .map(str -> str.split("")) .flatMap(strs -> //將strs後的表達式返回的stream都合併爲一個 Arrays.stream(strs).flatMap(f -> second.stream() .map(sec -> sec.split("")) .flatMap(secs -> Arrays.stream(secs).map(s -> "(" + f + "," + s + ")"))) ).collect(Collectors.toList()); collect.forEach(System.out::println); //耐心看.....
查找和匹配
- anyMatch:至少匹配一個
List<String> list = Arrays.asList("1","2","3","4");
boolean b = list.stream().anyMatch(s -> s.equals("1")); //true
- allMatch:全部匹配
List<String> list = Arrays.asList("1","2","3","4");
boolean b = list.stream().allMatch(s -> Integer.parseInt(s) < 5); //true
- noneMatch:全部不匹配
List<String> list = Arrays.asList("1","2","3","4");
boolean b = list.stream().noneMatch(s -> Integer.parseInt(s) > 5); //true
- 如上的方法都具有短路效果,意思就是主要遇到一個不匹配條件的元素就立刻返回true或者false
- findAny:找任意一個
List<String> list = Arrays.asList("1","2","3","4");
Optional<String> any = list.stream().findAny();
System.out.println(any.get()); //雖然書上說是任意一個,但是我一直返回的是1
//自己測試的將元素添加到十個,stream依舊是1,但是使用並行流parallelStream,將隨機返回,但是依然是區間比較小
- Option以後的帖子會說到的,你可以看一下我相關的帖子,只要記住Option是一個值的容器,因爲findAny找的可能是個空列表,所以他可能會返回null,然後將返回的這個值包裝在Option中,然後調用get方法就會出現返回的值,如果Option中沒有值還get那麼就會報錯,現在只要知道這點就可以了
- findFirst:找到第一個
List<String> list = Arrays.asList("1","2","3","4");
Optional<String> any = list.stream().findFirst();
System.out.println(any.get()); //1
歸約reduce
- 上面的映射和這詞提到的歸約可以一起使用,類似hadoop中的mr模型
- 元素求和
int[] is = {1,2,3,4,5,6,7,8,9};
Arrays.stream(is).reduce(0,Integer::sum);//45
//以0爲起始值,所以如果數組內沒有元素,也不至於null,然後依次相加
//0+1=1
//1+2=3
//3+3=6
//6+4=10 ....
int[] is = {1,2,3,4,5,6,7,8,9};
OptionalInt reduce = Arrays.stream(is).reduce(Integer::sum);
System.out.println("reduce = " + reduce.getAsInt());//45
//OptionalInt的出現就是因爲它沒有初始值進行累加,所以如果數組爲空,那麼將返回null
//OptionalInt使用方法和Optional一樣,只是方法名變了一下,目前只知道這些就好了
- 元素最大值和最小值
//max
int[] is = {1,2,3,4,5,6,7,8,9};
OptionalInt reduce = Arrays.stream(is).reduce(Integer::max);
System.out.println("reduce = " + reduce.getAsInt()); //9
//求最小改爲min方法即可,也可以自己實現
OptionalInt reduce = Arrays.stream(is).reduce((a,b) -> a > b ? a : b );
-
歸約方法的優勢和並行化
- 求和方法:定義一個int變量,然後迭代去加。相比之下reduce將其轉換爲了內部迭代。而且迭代要去求和並且更新我們的一個int共享變量,這對於並行化來說並不容易實現,如果加入了同步,可能線程切換的開銷就已經抵消了並行帶來的性能提升。(可變的變量累計器對於並行來說並不好),如上的代碼爲了實現並行只需要把stream方法改爲parallelStream()即可
int[] is = {1,2,3,4,5,6,7,8,9}; int sum = 0; //int共享變量 //這只是單線程的,如果是多線程的話,爲了保證sum的正確肯定要sync。 //所以這就是說的共享變量並不適合於並行化 for (int i : is) { sum += i; //更新共享變量 }
-
流操作的有狀態和無狀態
- 如果你購買了書可以先去看看書的定義,下面是我自己的理解
int[] is = {1,2,3,4,5,6,7,8,9}; Arrays.stream(is) .filter(i -> i > 3) //無狀態 .map(i -> i + 1) //無狀態 .distinct() //有狀態 .max(); //有狀態
- 對於上面來說,filter和map只是接收一個元素然後過濾映射一個元素,這個元素處理完就交給下面的方法處理了,自己並不保留這個元素,這樣的叫做無狀態
- 那distinct和max來說,他不能接收一個處理一個然後再交給下面的方法處理,因爲它需要拿到整個元素來去重和去最大值,如果拿到部分他肯定是不能做這些操作的,元素就暫時的保留在了方法中,所以這樣的叫做有狀態