?使用java8也有一段時間了,但是一直沒有整理過具體的筆記,現(xiàn)補充一下。
?java8流處理讓集合操作變得簡便了很多,通過我們在jdk7的時候,要進行很多行代碼的編寫才能完成的操作,使用流以后,可以在一行代碼之間實現(xiàn)。
?我們先來準備一些基礎(chǔ)數(shù)據(jù),便于后面的操作。
/**
實體類
**/
package java8;
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
public class People {
private int id;
private Stringname;
private int age;
private int score;
}
//main方法之前,先加載一些數(shù)據(jù)
static ListpeopleList =new ArrayList<>();
static ListsortList = Arrays.asList(1,3,8,6,7,9,10,5);
static {
People one =new People(1, "張三", 15, 88);
People two =new People(2, "李四", 14, 99);
People three =new People(3, "王五", 13, 100);
People four =new People(4, "趙六", 12, 85);
People five =new People(5, "王大麻子", 11, 55);
People six =new People(6, "田七", 10, 53);
People seven =new People(7, "小明", 14, 100);
People eight =new People(8, "小紅", 15, 75);
People nine =new People(9, "小藍", 17, 65);
People ten =new People(10, "小綠", 13, 66);
peopleList.add(one);
peopleList.add(two);
peopleList.add(three);
peopleList.add(four);
peopleList.add(five);
peopleList.add(six);
peopleList.add(seven);
peopleList.add(eight);
peopleList.add(nine);
peopleList.add(ten);
}
好了,數(shù)據(jù)我們已經(jīng)準備好了,那么就開始下一步吧。先從簡單的開始說起。
- forEach
//最簡單的遍歷,就是把當前peopleList循環(huán)出來,并打印。之前我們最常用的就是for循環(huán),現(xiàn)在一行就可以搞定了。
peopleList.stream().forEach(people -> System.out.println(people));
2.findAny, findFirst,從語意上可以看出來,兩個方法都是查找。findAny就是查找符合條件的任意一條;findFirst查找符合條件的第一條。l因findFirst和findAny的使用方式,完全一致,就不再重復(fù)了。
Optional<People> any = peopleList.stream().findAny();
if(any.isPresent()){
System.out.println(any.get());
}
//orElse和orElseGet這兩個方法看起來差不多.區(qū)別在于:不論findAny返回的結(jié)果是不是空,orElse都會執(zhí)行,而orElseGet則當findAny為空時才會執(zhí)行。
//只有當默認值已經(jīng)事先定義的情況下,才使用orElse(),否則使用orElseGet()更好。
People people = peopleList.stream().findAny().orElse(new People(12, "ls", 13, 66));
System.out.println(people);
People people1 = peopleList.stream().findAny().orElseGet(()->new People(11, "zs", 12, 22));
System.out.println(people1);
//獲取不到則拋出異常,這個異常信息,根據(jù)業(yè)務(wù)需要來就可以了。
People people2 = peopleList.stream().findAny().orElseThrow(RuntimeException::new);
System.out.println(people2.toString());
- filter過濾,根據(jù)條件進行篩選數(shù)據(jù)。
//filter 根據(jù)自己的需求過濾出自己所需要的內(nèi)容,比如過濾出年齡大于15歲的people
List<People> filterPeople = peopleList.stream().filter(people -> people.getAge() > 15).collect(Collectors.toList());
filterPeople.stream().forEach(System.out::println);
4.map和flatMap,map是一維映射,也就是說,只有一層的List,使用其中的對象的某個屬性是可以的。但是如果List嵌套,則只有第一層才能被映射。如果你想把兩層List都映射出來。那么就請使用flatMap.
//map 抽取對象的某一個屬性變成新的List,用名字形成一個新的List(一維,也就是說,只能使用一層List,如果List里面放一個List,此時map就只能把第一層List映射出來)
List<String> peopleNamesList = peopleList.stream().map(People::getName).collect(Collectors.toList());
peopleNamesList.stream().forEach(System.out::println);
//flatMap 跟map的區(qū)別就在于,flatMap可以把多層次的對象給映射出來
List<List<Integer>> outer = new ArrayList<>();
List<Integer> inner1 = new ArrayList<>();
inner1.add(1);
List<Integer> inner2 = new ArrayList<>();
inner2.add(2);
List<Integer> inner3 = new ArrayList<>();
inner3.add(3);
List<Integer> inner4 = new ArrayList<>();
inner4.add(4);
List<Integer> inner5 = new ArrayList<>();
inner5.add(5);
outer.add(inner1);
outer.add(inner2);
outer.add(inner3);
outer.add(inner4);
outer.add(inner5);
List<Integer> result = outer.stream().flatMap(List::stream).collect(toList());
System.out.println(result);
5.sorted 排序,分別使用了對象的屬性排序,和Integer的排序。
//sorted 排序,默認是正序,可以指定對象的任意屬性來進行排序。.reversed是反轉(zhuǎn)的意思,正序的反轉(zhuǎn)就相當于倒序排序。如果是基礎(chǔ)數(shù)據(jù)類型,則可以直接使用sorted來排序,沒有任何參數(shù)的時候,相當于傳入Comparator.naturalOrder(),即正序排序。Comparator.reverseOrder(), 則是倒序排序。
peopleList.stream().sorted(Comparator.comparing(People::getId).reversed()).forEach(System.out::println);
sortList.stream().sorted(Comparator.reverseOrder()).forEach(System.out::println);
//寫到這里,不得不提一下我之前犯下的一個錯誤。集合的sort()是非線程安全的。
for (int i = 0; i < 10; i++) {
new Thread(() -> {
peopleList.sort((o1,o2) -> o1.getScore()-o2.getScore());
}).start();
}

//而 stream 的 sorted是線程安全的。
for (int i = 0; i < 10; i++) {
new Thread(() -> {
peopleList.stream().sorted((o1,o2) -> o1.getScore()-o2.getScore()).forEach(System.out::println);
}).start();
}
下面這種寫法就可以正常返回結(jié)果。
- collect 一般是一個stream操作的最后一步,把最終結(jié)果轉(zhuǎn)成map或者list或者set。
//collect 一般是一個stream操作的最后一步,把最終結(jié)果轉(zhuǎn)成map或者list或者set。
//其中轉(zhuǎn)成map的時候,有g(shù)roupingBy和groupingByConcurrent(可以利用到多核架構(gòu)的能力。groupingByConcurrent也有3個重載的方法,與groupingBy類似。但返回值必須是ConcurrentHashMap或其子類.)
//轉(zhuǎn)成list
peopleList.stream().collect(Collectors.toList());
//轉(zhuǎn)成Map
Map<Integer, List<People>> collect = peopleList.stream().collect(Collectors.groupingBy(people -> people.getAge()));
collect.entrySet().stream().forEach(entry->{
System.out.println(entry.getKey());
System.out.println(entry.getValue().size());
});
//按照多個字段進行分組
Map<Integer, Map<Integer, List<People>>> mutGroupMap = peopleList.stream().collect(Collectors.groupingBy(People::getAge, Collectors.groupingBy(People::getScore)));
mutGroupMap.entrySet().stream().forEach(entry->{
System.out.println(entry.getKey());
System.out.println(entry.getValue().size());
});
//注意,當我們的集合當中有重復(fù)數(shù)據(jù)的時候,groupingby會把結(jié)果改為數(shù)組:
peopleList.add(new People(10,"小綠", 13,66));
Map<String, Map<Integer, List<People>>> flatMap = peopleList.stream().collect(Collectors.groupingBy(People::getName, Collectors.groupingBy(People::getScore)));
flatMap.entrySet().stream().forEach(entry->{
System.out.println(entry.getKey());
System.out.println(entry.getValue());
});

groupingby 優(yōu)點:非常適合分組歸類使用,方便快捷。
缺點也很明顯,當分組的條件多的時候,這個Map就難以閱讀,維護了。
groupingby不支持null鍵
//toMap 速度快,支持一個null鍵
//添加一個重復(fù)數(shù)據(jù)
peopleList.add(new People(10,"小綠", 13,66));
//報異常
Map<String, Integer> toMap2 = peopleList.stream().collect(Collectors.toMap(People::getName, People::getAge));
toMap2.entrySet().stream().forEach(entry -> {
System.out.println(entry.getKey() + "--" + entry.getValue());
});
//Exception in thread "main" java.lang.IllegalStateException: Duplicate key 13
//at java.util.stream.Collectors.lambda$throwingMerger$0(Collectors.java:133)
//at java.util.HashMap.merge(HashMap.java:1254)
//at java.util.stream.Collectors.lambda$toMap$58(Collectors.java:1320)
//at java.util.stream.ReduceOps$3ReducingSink.accept(ReduceOps.java:169)
//at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1382)
//at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:482)
//at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:472)
//at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
//at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
//at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:499)
//at com.hexy.java8.GroupingByAndToMap.main(GroupingByAndToMap.java:43)
Map<String, Integer> toMap = peopleList.stream().collect(Collectors.toMap(People::getName, People::getAge ,(oldValue, newValue) -> oldValue , HashMap::new));
toMap.entrySet().stream().forEach(entry -> {
System.out.println(entry.getKey() + "--" + entry.getValue());
});
//正常打印,我們來看一下,如果我們不寫第三個參數(shù),那jdk默認幫我們做了什么吧?


7.max min 根據(jù)對象的某個屬性取最大值或者最小值
//max min 根據(jù)對象的某個屬性取最大值或者最小值,比如取分數(shù)最高的,和分數(shù)最低的 具體的對比規(guī)則,可以直接使用Comparator傳入
Optional<People> maxScore = peopleList.stream().max(Comparator.comparing(People::getScore));
Optional<People> minScore = peopleList.stream().min(Comparator.comparing(People::getScore));
System.out.println(maxScore.get());
System.out.println(minScore.get());
- limit 這個跟sql的limit很類似。只取前N個。
//limit 限制取 limit個 成績大于90的前兩個(和一個例子,只是找出來了成績大于90分的人,但是并沒有數(shù)量上的限制,所以找出來3人,但是第二個,由于寫了limit(2),則就是限制,我只取兩條)
List<People> greatThanNinetyList = peopleList.stream().filter(people -> people.getScore() > 90).collect(Collectors.toList());
System.out.println(greatThanNinetyList.size());
List<People> greatThanNinetyLimitTwoList = peopleList.stream().filter(people -> people.getScore() > 90).limit(2).collect(Collectors.toList());
System.out.println(greatThanNinetyLimitTwoList.size());
9.count 集合的數(shù)量
System.out.println(peopleList.stream().count());
10.distinct 這個跟sql的distinct也很類似
//去重, 我們再加入一個小綠 10, "小綠", 13, 66
peopleList.add(new People(10, "小綠", 13, 66));
//應(yīng)該有11個
System.out.println("distinct前的數(shù)量:" + peopleList.size());
//有10個
System.out.println("distinct后的數(shù)量:" + peopleList.stream().distinct().collect(Collectors.toList()).size());
11.allMatch(全部符合設(shè)置的條件) anyMatch(任意一個符合設(shè)置的條件) noneMatch(沒有一個符合設(shè)置的條件)返回值為boolean
// allMatch(全部符合設(shè)置的條件) anyMatch(任意一個符合設(shè)置的條件) noneMatch(沒有一個符合設(shè)置的條件)
boolean allMatch = peopleList.stream().allMatch(people -> people.getScore()>99);
System.out.println(allMatch);
boolean anyMatch = peopleList.stream().anyMatch(people -> people.getScore()>99);
System.out.println(anyMatch);
boolean noneMatch = peopleList.stream().anyMatch(people -> people.getScore()>100);
System.out.println(noneMatch);
12.skip 跳過指定個數(shù)的元素
//只打印出peopleList的后7個元素
peopleList.stream().skip(3).forEach(System.out::println);
13.peek 是一個中間操作,它一般用于調(diào)試,或者你需要修改stream的元素的時候,使用它比較合適。因為它改變了數(shù)據(jù)之后,并不會消耗掉stream,你可以繼續(xù)的執(zhí)行后面的操作。
peopleList.stream().peek(people -> {
if(people.getScore() == 100){
people.setScore(1000);
}
}).forEach(System.out::println);
13.foreachOrdered和unordered 在并發(fā)情況下,forechOrdered是必須按照集合的順序來循環(huán)。而unordered則是在并發(fā)條件下,取消必須按集合順序循環(huán)。
//foreachOrdered 嚴格按照list的順序輸出(一般只有并行的時候用)
peopleList.stream().parallel().forEach(System.out::println);
System.out.println("----------------------------");
// 取消嚴格按照順序循環(huán),有利于執(zhí)行效率的提高
peopleList.stream().unordered().parallel().forEach(System.out::println);
- reduce
//reduce 1.第一個重載函數(shù)的入?yún)⑹且粋€BinaryOperator的函數(shù)式接口(該函數(shù)式接口接收兩個T類型的參數(shù)并返回T類型)。該方法返回的是一個Optional類型的值。
sortList.stream().reduce((item, next)-> item + next).ifPresent(System.out::println);
//reduce 2.
Integer reduce = sortList.stream().reduce(0, (item, next) -> item + next);
System.out.println(reduce);
//reduce 3.
Integer reduce1 = sortList.stream().reduce(0, (item, next) -> item + next, (item, next) -> item);
System.out.println(reduce1);
15.parallel 開啟并行操作
//parallel 把當前流設(shè)置為并行,isParallel是檢測當前Stream是否是并行。
//false 因為沒有開啟并行
System.out.println(peopleList.stream().isParallel());
//true 因為手動開啟了并行
System.out.println(peopleList.stream().parallel().isParallel());
16.sequential 返回串行的流,也就是取消并行
peopleList.stream().sequential();
17.Spliterator是一個可分割迭代器(splitable iterator),對于并行處理的能力大大增強,Spliterator就是為了并行遍歷元素而設(shè)計的一個迭代器,jdk1.8中的集合框架中的數(shù)據(jù)結(jié)構(gòu)都默認實現(xiàn)了spliterator。
Spliterator<People> mySpliterator = peopleList.stream().spliterator();
AtomicInteger count = new AtomicInteger(0);
for(int i=0;i<4;i++){
new Thread(()->{
String threadName = Thread.currentThread().getName();
System.out.println("線程"+threadName+"開始運行-----");
mySpliterator.trySplit().forEachRemaining(people -> {
if(people.getScore()>80){
int num = people.getScore();
count.addAndGet(num);
System.out.println("數(shù)值:"+num+"------"+threadName);
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
System.out.println("線程"+threadName+"運行結(jié)束-----");
}).start();
}
//得到的結(jié)果
線程Thread-2開始運行-----
線程Thread-0開始運行-----
數(shù)值:100------Thread-0
數(shù)值:88------Thread-2
線程Thread-3開始運行-----
線程Thread-1開始運行-----
線程Thread-1運行結(jié)束-----
線程Thread-3運行結(jié)束-----
線程Thread-0運行結(jié)束-----
數(shù)值:99------Thread-2
數(shù)值:100------Thread-2
數(shù)值:85------Thread-2
線程Thread-2運行結(jié)束-----