Java8的Stream API使用
前言
這次想介紹一下Java Stream的API使用,最近在做一個(gè)新的項(xiàng)目,然后終于可以從老項(xiàng)目的祖?zhèn)鞔a坑里跳出來了。項(xiàng)目用公司自己的框架搭建完成后,我就想著把JDK版本也升級(jí)一下吧(之前的項(xiàng)目,最高就能用JDK7),但是后來發(fā)現(xiàn)公司的項(xiàng)目部署打包平臺(tái)最高只支持到JDK8。那好吧,既然就支持到JDK8,也能滿足日常需求了(要啥自行車),升級(jí)到JDK8后,在搭建完項(xiàng)目架構(gòu)后,就開始寫一些基礎(chǔ)邏輯。其中就用到了一些JDK8的Stream。但是我的同事在看我的代碼的時(shí)候表示看不懂。確實(shí),這個(gè)我也承認(rèn),Lambda表達(dá)式雖然代碼簡潔,但是不會(huì)用的人會(huì)覺得它的可讀性不是太好。所以這次就結(jié)合自己使用經(jīng)驗(yàn)來介紹一下Java Stream的一些功能。
從遍歷到Stream操作
Oracle 公司于 2014 年 3 月 18 日發(fā)布 Java 8,Java8主要是在原來面向?qū)ο蟮幕A(chǔ)上增加了函數(shù)式編程的能力。這樣就出現(xiàn)了在Java中使用Lambda表達(dá)式,將一個(gè)函數(shù)作為方法的參數(shù)來進(jìn)行傳遞。Java8的Stream就是典型的例子,Stream API可以極大提高Java程序員的生產(chǎn)力,讓程序員寫出高效率、干凈、簡潔的代碼。
例子:
List<Integer> numbers = new ArrayList<>();
numbers.add(3);
numbers.add(4);
numbers.add(8);
numbers.add(16);
numbers.add(19);
numbers.add(27);
numbers.add(23);
numbers.add(99);
numbers.add(15);
numbers.add(32);
numbers.add(5);
numbers.add(232);
numbers.add(56);
int count = 0;
for(Integer i:numbers){
if(i>20){
count++;
}
}
System.out.println("count:"+count);
如上遍歷的代碼轉(zhuǎn)換成使用Stream的API來實(shí)現(xiàn)如下:
long count = numbers.stream().filter(i->i>20).count(); System.out.println("count:"+count);
正常的遍歷用Stream一行就可以實(shí)現(xiàn)了。
下面是一個(gè)使用了Stream API實(shí)現(xiàn)的流程圖。

轉(zhuǎn)換成Java代碼就是
Integer transactionsIds = roomList.stream() .filter(b -> b.getLength() == 10) .sorted((x,y) -> x.getHigh() - y.getHigh()) .mapToInt(Room::getWidth).sum();
創(chuàng)建Stream
Arrays.stream()
當(dāng)在日常編程中面對(duì)的是一個(gè)數(shù)組,也可以使用Arrays.stream()方法來使用Stream
Integer[] array = new Integer[]{3,4,8,16,19,27,23,99,76,232,33,96}; long count = Arrays.stream(array).filter(i->i>20).count();
Stream.of()
當(dāng)面對(duì)數(shù)組時(shí)除了可以使用Arrays.stream()方法外,還可以使用Stream將需要的數(shù)組轉(zhuǎn)成Stream。這個(gè)方法不但支持傳入數(shù)組,將數(shù)組轉(zhuǎn)成Stream,也支持傳入多個(gè)參數(shù),將參數(shù)最終轉(zhuǎn)成Stream
Integer[] array = new Integer[]{3,4,8,16,19,27,23,99,76,232,33,96}; long count = Stream.of(array).filter(i->i>20).count(); long sum = Stream.of(12,77,59,3,654).filter(i->i>20).mapToInt(Integer::intValue).sum(); System.out.println("count:"+count+",sum:"+sum);
其實(shí)Stream.of()也是調(diào)用的Stream.of()方法來實(shí)現(xiàn)的。
Stream.generate()
Stream<String> stream = Stream.generate(() -> "test").limit(10); String[] strArr = stream.toArray(String[]::new); System.out.println(Arrays.toString(strArr));
運(yùn)行結(jié)果
[test, test, test, test, test, test, test, test, test, test]
Stream.iterate()
Stream接口的另一用來創(chuàng)建無限Stream的靜態(tài)方法就是iterate()方法。iterate()方法也是接受一個(gè)參數(shù)函數(shù),可以用類似如下代碼來創(chuàng)建一個(gè)你需要的Stream。
Stream<BigInteger> bigIntStream = Stream.iterate(BigInteger.ZERO, n -> n.add(BigInteger.TEN)).limit(10); BigInteger[] bigIntArr = bigIntStream.toArray(BigInteger[]::new); System.out.println(Arrays.toString(bigIntArr));
運(yùn)行結(jié)果
[0, 10, 20, 30, 40, 50, 60, 70, 80, 90]
Collection.stream()
這個(gè)就是最常見的Stream了。因?yàn)镃ollection是Java中集合接口的父接口,Java中的集合都繼承或?qū)崿F(xiàn)了此接口。所以Java中的集合都可以使用此方法來創(chuàng)建一個(gè)Stream;
/** * @see Set * @see List * @see Map * @see SortedSet * @see SortedMap * @see HashSet * @see TreeSet * @see ArrayList * @see LinkedList * @see Vector * @see Collections * @see Arrays * @see AbstractCollection * @since 1.2 */ public interface Collection<E> extends Iterable<E> { /** * Returns a sequential {@code Stream} with this collection as its source. * * <p>This method should be overridden when the {@link #spliterator()} * method cannot return a spliterator that is {@code IMMUTABLE}, * {@code CONCURRENT}, or <em>late-binding</em>. (See {@link #spliterator()} * for details.) * * @implSpec * The default implementation creates a sequential {@code Stream} from the * collection's {@code Spliterator}. * * @return a sequential {@code Stream} over the elements in this collection * @since 1.8 */ default Stream<E> stream() { return StreamSupport.stream(spliterator(), false); } }
例子
List<Integer> numbers = new ArrayList<>(); numbers.add(3); numbers.add(4); numbers.add(8); numbers.add(16); numbers.stream().forEach(number->{ System.out.println(number); });
StreamSupport.stream()
通過查看Collection.stream()的方法,我們可以看出來,Colleciton.stream()其實(shí)是調(diào)用了StreamSupport.stream()來實(shí)現(xiàn)的。所以我們也可以使用StreamSupport.stream()來創(chuàng)建一個(gè)Stream。當(dāng)我們面對(duì)的是一個(gè)迭代器的時(shí)候,使用StreamSupport.stream()就可以創(chuàng)建一個(gè)Stream。第一個(gè)參數(shù)是傳入一個(gè)迭代器,第二個(gè)參數(shù)是true代表使用并行來進(jìn)行處理。false代表串行來處理Stream。
List<Integer> numbers = new ArrayList<>();
numbers.add(3); numbers.add(4); numbers.add(8); numbers.add(16); numbers.add(19); numbers.add(27); numbers.add(23); Spliterator<Integer> integers = numbers.spliterator(); StreamSupport.stream(integers,false).forEach(number->{ System.out.println(number); });
流的轉(zhuǎn)換
filter方法
從名字上就能看出來,這是一個(gè)Stream的過濾轉(zhuǎn)換,此方法會(huì)生成一個(gè)新的流,其中包含符合某個(gè)特定條件的所有元素。
List<Integer> integerList = Lists.newArrayList();
integerList.add(15);
integerList.add(32);
integerList.add(5);
integerList.add(232);
integerList.add(56);
List<Integer> after = integerList.stream()
.filter(i->i>50)
.collect(Collectors.toList());
System.out.println(after);
運(yùn)行結(jié)果:
[232, 56]
map方法
map方法指對(duì)一個(gè)流中的值進(jìn)行某種形式的轉(zhuǎn)換。需要傳遞給它一個(gè)轉(zhuǎn)換的函數(shù)作為參數(shù)。
List<Integer> integerList = Lists.newArrayList();
integerList.add(15);
integerList.add(32);
integerList.add(5);
integerList.add(232);
integerList.add(56);
//將Integer類型轉(zhuǎn)換成String類型
List<String> afterString = integerList.stream()
.map(i->String.valueOf(i)).collect(Collectors.toList());
System.out.println(afterString);
flatMap方法
上面用map方法進(jìn)行流轉(zhuǎn)換的時(shí)候,是對(duì)每個(gè)元素應(yīng)用一個(gè)函數(shù),并將返回的值收集到一個(gè)新的流中。但是如果有一個(gè)函數(shù),它返回的不是一個(gè)值,而是一個(gè)包含多個(gè)值的流。但是你需要的是一個(gè)包含多個(gè)流中的元素的集合。
例如
List<Integer> oneList = Lists.newArrayList(),
twoList = Lists.newArrayList();
oneList.add(34);
oneList.add(23);
oneList.add(87);
twoList.add(29);
twoList.add(48);
twoList.add(92);
Map<String,List<Integer>> testMap = Maps.newHashMap();
testMap.put("1",oneList);
testMap.put("2",twoList);
//返回的是一個(gè)流的集合,但是我需要的是List<Integer>這樣一個(gè)集合
List<Stream<Integer>> testList = testMap.values().stream()
.map(number->number.stream()).collect(Collectors.toList());
這個(gè)時(shí)候就應(yīng)該使用flatMap將多個(gè)流進(jìn)行合并,然后再收集到一個(gè)集合中。
List<Integer> testList = testMap.values().stream()
.flatMap(number->number.stream()).collect(Collectors.toList());
limit方法和skip方法
limit(n)方法會(huì)返回一個(gè)包含n個(gè)元素的新的流(若總長小于n則返回原始流)。
List<Integer> myList = Lists.newArrayList();
myList.add(1);
myList.add(2);
myList.add(3);
myList.add(4);
myList.add(5);
myList.add(6);
List<Integer> afterLimit = myList.stream().limit(4).collect(Collectors.toList());
System.out.println("afterLimit:"+afterLimit);
skip(n)方法正好相反,它會(huì)丟棄掉前面的n個(gè)元素。
List<Integer> afterSkip = myList.stream().skip(4).collect(Collectors.toList());
System.out.println("afterSkip:"+afterSkip);
運(yùn)行結(jié)果:
afterLimit:[1, 2, 3, 4]
afterSkip:[5, 6]
用limit和skip方法一起使用就可以實(shí)現(xiàn)日常的分頁功能:
List<Integer> pageList = myList.stream()
.skip(pageNumber*pageSize)
.limit(pageSize).collect(Collectors.toList());
distinct方法和sorted方法
上面介紹的流的轉(zhuǎn)換方法都是無狀態(tài)的。即從一個(gè)已經(jīng)轉(zhuǎn)換的流中取某個(gè)元素時(shí),結(jié)果并不依賴于之前的元素。除此之外還有兩個(gè)方法在轉(zhuǎn)換流時(shí)是需要依賴于之前流中的元素的。一個(gè)是distinct方法一個(gè)是sorted方法。
distinct方法會(huì)根據(jù)原始流中的元素返回一個(gè)具有相同順序、去除了重復(fù)元素的流,這個(gè)操作顯然是需要記住之前讀取的元素。
List<Integer> myTestList = Lists.newArrayList();
myTestList.add(10);
myTestList.add(39);
myTestList.add(10);
myTestList.add(78);
myTestList.add(10);
List<Integer> distinctList = myTestList.stream()
.distinct().collect(Collectors.toList());
System.out.println("distinctList:"+distinctList);
運(yùn)行結(jié)果:
distinctList:[10, 39, 78]
sorted方法是需要遍歷整個(gè)流的,并在產(chǎn)生任何元素之前對(duì)它進(jìn)行排序。因?yàn)橛锌赡芘判蚝蠹系牡谝粋€(gè)元素會(huì)在未排序集合的最后一位。
List<Integer> myTestList = Lists.newArrayList(); myTestList.add(39); myTestList.add(78); myTestList.add(10); myTestList.add(22); myTestList.add(56); List<Integer> sortList = myTestList.stream()
.sorted(Integer::compareTo).collect(Collectors.toList()); System.out.println("sortList:"+sortList);
運(yùn)行結(jié)果:
sortList:[10, 22, 39, 56, 78]
聚合操作
前面已經(jīng)介紹了流的創(chuàng)建和轉(zhuǎn)換,下面介紹流的聚合,聚合是指將流匯聚為一個(gè)值,以便在程序中使用。聚合方法都是終止操作。
max方法和min方法
在前面的代碼例子中使用的count方法和sum方法都屬于流從聚合方法。還有兩個(gè)聚合方法是max方法和min方法,分別返回流中最大值和最小值。
List<Integer> hearList = Lists.newArrayList();
hearList.add(15);
hearList.add(32);
hearList.add(5);
hearList.add(232);
hearList.add(56);
hearList.add(29);
hearList.add(94);
Integer maxItem = hearList.stream().max(Integer::compareTo).get();
Integer minItem = hearList.stream().min(Integer::compareTo).get();
System.out.println("max:"+maxItem+",min:"+minItem);
運(yùn)行結(jié)果:
max:232,min:5
findFirst方法
findFirst方法返回非空集合中的第一個(gè)值,它通常與filter方法結(jié)合起來使用。
List<Integer> hearList = Lists.newArrayList();
hearList.add(15);
hearList.add(32);
hearList.add(5);
hearList.add(232);
hearList.add(56);
hearList.add(29);
hearList.add(104);
Integer first = hearList.stream().filter(i->i>100).findFirst().get();
findAny方法
findAny方法可以在集合中只要找到任何一個(gè)所匹配的元素,就返回,此方法在對(duì)流并行執(zhí)行時(shí)十分有效(任何片段中發(fā)現(xiàn)第一個(gè)匹配元素都會(huì)結(jié)束計(jì)算,串行流中和findFirst返回一樣)。
Integer anyItem = hearList.parallelStream().filter(i->i>100).findAny().get();
anyMatch方法
anyMatch方法可以判定集合中是否還有匹配的元素。返回結(jié)果是一個(gè)boolean類型值。
boolean isHas = hearList.parallelStream().anyMatch(i->i>100);
allMatch方法和noneMatch方法
allMatch方法和noneMatch方法,分別在所有元素匹配和沒有元素匹配時(shí)返回true。
boolean allHas = hearList.parallelStream().allMatch(i->i>100); boolean noHas = hearList.parallelStream().noneMatch(i->i>100);
雖然這些方法總是會(huì)檢查整個(gè)流,但是仍然可以通過并行執(zhí)行來提高速度。
reduce方法
reduce方法是將流中的元素進(jìn)行進(jìn)一步計(jì)算的方法。
List<Integer> hearList = Lists.newArrayList(); hearList.add(15); hearList.add(32); hearList.add(5); hearList.add(232); hearList.add(56); hearList.add(29); hearList.add(104); //求和 Integer sum = hearList.stream().reduce((x,y)->x+y).get(); System.out.println("sum:"+sum); //簡化一下,求和 sum = hearList.stream().reduce(Integer::sum).get(); System.out.println("sum:"+sum); //含有初始標(biāo)識(shí)的,求和 sum = hearList.stream().reduce(0,(x,y)->x+y); System.out.println("sum:"+sum); //對(duì)元素的長度進(jìn)行求和( (total,y)->total+y.toString().length(),類似于一個(gè)累加器,會(huì)被重復(fù)調(diào)用) sum = hearList.stream().reduce(0,(total,y)->total+y.toString().length(),(total1,total2)->total1+total2); System.out.println("sum:"+sum); //簡化一下,對(duì)元素長度進(jìn)行求和。 sum = hearList.stream().map(Objects::toString).mapToInt(String::length).sum(); System.out.println("sum:"+sum);
運(yùn)行結(jié)果
sum:473 sum:473 sum:473 sum:15 sum:15
收集結(jié)果
當(dāng)處理完流之后,通常是想查看一下結(jié)果,而不是將他們聚合為一個(gè)值。Collectorts類為我們提供了常用的收集類的各個(gè)工廠方法。
收集到集合
例如前面的例子用的要將一個(gè)流收集到一個(gè)List中,只需要這樣寫就可以。
List<Integer> thereList = hereList.stream().collect(Collectors.toList());
收集到Set中可以這樣用
Set<Integer> thereSet = hereList.stream().collect(Collectors.toSet());
收集到Set時(shí),控制Set的類型,可以這樣。
TreeSet<Integer> treeSet = hereList.stream()
.collect(Collectors.toCollection(TreeSet::new));
拼接
將字流中的字符串連接并收集起來。
String resultString = stringList.stream().collect(Collectors.joining());
在將流中的字符串連接并收集起來時(shí),想在元素中介添加分隔符,傳遞個(gè)joining方法即可。
String resultString = stringList.stream().collect(Collectors.joining(","));
當(dāng)流中的元素不是字符串時(shí),需要先將流轉(zhuǎn)成字符串流再進(jìn)行拼接。
String hereResultString = hereList.stream()
.map(String::valueOf).collect(Collectors.joining(","));
收集聚合
分別收集流的總和、平均值、最大值或者最小值。
List<Integer> hereList = Lists.newArrayList(); hereList.add(15); hereList.add(32); hereList.add(5); hereList.add(232); hereList.add(56); hereList.add(29); hereList.add(104); //總和、平均值,最大值,最小值 int sum = hereList.stream().collect(Collectors.summingInt(Integer::intValue)); Double ave = hereList.stream().collect(Collectors.averagingInt(Integer::intValue)); Integer max = hereList.stream().collect(Collectors.maxBy(Integer::compare)).get(); Integer min = hereList.stream().collect(Collectors.minBy(Integer::compare)).get(); System.out.println("sum:"+sum+",ave:"+ave+",max:"+max+",min:"+min);
運(yùn)行結(jié)果:
sum:473,ave:67.57142857142857,max:232,min:5
一次性收集流中的結(jié)果,聚合為一個(gè)總和,平均值,最大值或最小值的對(duì)象。
IntSummaryStatistics summaryStatistics = hereList.stream()
.collect(Collectors.summarizingInt(Integer::intValue));
System.out.println(summaryStatistics);
運(yùn)行結(jié)果:
IntSummaryStatistics{count=7, sum=473, min=5, average=67.571429, max=232}
將結(jié)果集收集到Map
當(dāng)我們希望將集合中的元素收集到Map中時(shí),可以使用Collectors.toMap方法。這個(gè)方法有兩個(gè)參數(shù),用來生成Map的key和value。
例如將一個(gè)Room對(duì)象的high作為鍵width作為值

Map<Integer,Integer> hwMap = roomList.stream()
.collect(Collectors.toMap(Room::getHigh, Room::getWidth));
但是通常還是以具體元素作為值的情況多,可以使用Function.identity()來獲取實(shí)際元素。
Map<Integer,Room> roomMap = roomList.stream()
.collect(Collectors.toMap(Room::getHigh, Function.identity()));
如果多個(gè)元素?fù)碛邢嗤逆I,在收集結(jié)果時(shí)會(huì)拋出java.lang.IllegalStateException異常。可以使用第三個(gè)參數(shù)來解決,第三個(gè)參數(shù)用來確定當(dāng)出現(xiàn)鍵沖突時(shí),該如何處理結(jié)果,如果當(dāng)出現(xiàn)鍵沖突時(shí)只保留一個(gè)并且是保留已經(jīng)存在的值時(shí),就是如下方式。
Map<Integer,Room> rMap = roomList.stream()
.collect(Collectors.toMap(Room::getHigh, Function.identity(),(nowValue,newValue)->nowValue));
如果想指定生成的Map類型,則還需要第三個(gè)參數(shù)。
TreeMap<Integer,Room> roomTreeMap = roomList.stream() .collect(Collectors.toMap(Room::getHigh,
Function.identity(),(nowValue,newValue)->newValue,TreeMap::new));
注意:每個(gè)toMap方法,都會(huì)有一個(gè)對(duì)應(yīng)的toConCurrentMap方法,用來生成一個(gè)并發(fā)Map。
分組分片
在一個(gè)集合中,對(duì)具有相同特性的值進(jìn)行分組是一個(gè)很常見的功能,在Stream的API中也提供了相應(yīng)的方法。
分組
還是上面的例子,將一個(gè)Room對(duì)象集合按照高度分組。
List<Room> roomList = Lists.newArrayList( new Room(11,23,56), new Room(11,84,48), new Room(22,46,112), new Room(22,75,62), new Room(22,56,75), new Room(33,92,224)); Map<Integer,List<Room>> groupMap = roomList.stream().collect(Collectors.groupingBy(Room::getHigh)); System.out.println("groupMap:"+groupMap);
運(yùn)行結(jié)果:
groupMap:{33=[Room(high=33, width=92, length=224)],
22=[Room(high=22, width=46, length=112), Room(high=22, width=75, length=62), Room(high=22, width=56, length=75)],
11=[Room(high=11, width=23, length=56), Room(high=11, width=84, length=48)]}
分片
當(dāng)分類函數(shù)是一個(gè)返回布爾值的函數(shù)時(shí),流元素會(huì)被分為兩組列表:一組是返回true的元素集合,另一組是返回false的元素集合。這種情況適用partitoningBy方法會(huì)比groupingBy更有效率。
例如我們將房間集合分為兩組,一組是高度為22的房間,另一組是其他房間。
Map<Boolean,List<Room>> partitionMap = roomList.stream()
.collect(Collectors.partitioningBy(room->room.getHigh()==22));
運(yùn)行結(jié)果:
partitionMap:{false=[Room(high=11, width=23, length=56), Room(high=11, width=84, length=48), Room(high=33, width=92, length=224)],
true=[Room(high=22, width=46, length=112), Room(high=22, width=75, length=62), Room(high=22, width=56, length=75)]}
擴(kuò)展功能
下面要介紹的這些方法功能,無論是groupingBy方法還是partitioningBy方法都是支持的。
counting方法會(huì)返回收集元素的總個(gè)數(shù)。
Map<Integer,Long> countMap = roomList.stream()
.collect(Collectors.groupingBy(Room::getHigh,Collectors.counting()));
summing(Int|Long|Double)方法接受一個(gè)取值函數(shù)作為參數(shù),來計(jì)算總和。
Map<Integer,Integer> sumMap = roomList.stream().
collect(Collectors.groupingBy(Room::getHigh,Collectors.summingInt(Room::getWidth)));
maxBy方法和minBy方法接受比較器作為參數(shù)來計(jì)算最大值和最小值。
取出分組中寬度最大和最小的房間。
Map<Integer, Optional<Room>> maxMap = roomList.stream(). collect(Collectors.groupingBy(Room::getHigh, Collectors.maxBy(Comparator.comparing(Room::getWidth)) )); Map<Integer, Optional<Room>> minMap = roomList.stream(). collect(Collectors.groupingBy(Room::getHigh, Collectors.minBy(Comparator.comparing(Room::getWidth)) )); System.out.println("maxMap:"+ JSON.toJSONString(maxMap)); System.out.println("minMap:"+JSON.toJSONString(minMap));
運(yùn)行結(jié)果:
maxMap:{33:{"high":33,"length":224,"width":92},22:{"high":22,"length":62,"width":75},11:{"high":11,"length":48,"width":84}}
minMap:{33:{"high":33,"length":224,"width":92},22:{"high":22,"length":112,"width":46},11:{"high":11,"length":56,"width":23}}
mapping方法會(huì)將結(jié)果應(yīng)用到另一個(gè)收集器上。
取出分組中寬度最大的房間的寬度。
Map<Integer, Optional<Integer>> collect = roomList.stream().collect(Collectors.groupingBy(Room::getHigh,
Collectors.mapping(Room::getWidth,
Collectors.maxBy(Comparator.comparing(Integer::valueOf)))));
System.out.println("collect:"+JSON.toJSONString(collect));
運(yùn)行結(jié)果:
collect:{33:92,22:75,11:84}
無論groupingBy或是mapping函數(shù),如果返回類型是int、long、double都可以將元素收集到一個(gè)summarystatistics對(duì)象中,然后從每組的summarystatistics對(duì)象中取出函數(shù)值的總和、平均值、總數(shù)、最大值和最小值。
Map<Integer,IntSummaryStatistics> summaryStatisticsMap = roomList.stream()
.collect(Collectors.groupingBy(Room::getHigh,
Collectors.summarizingInt(Room::getWidth)));
System.out.println("summaryStatisticsMap:"+summaryStatisticsMap);
運(yùn)行結(jié)果:
summaryStatisticsMap:{33=IntSummaryStatistics{count=1, sum=92, min=92, average=92.000000, max=92},
22=IntSummaryStatistics{count=3, sum=177, min=46, average=59.000000, max=75},
11=IntSummaryStatistics{count=2, sum=107, min=23, average=53.500000, max=84}}
多級(jí)分組
上面的例子我們都是按一個(gè)條件進(jìn)行的一級(jí)分組,其實(shí)groupingBy是支持多級(jí)分組的。
例如第一級(jí)我們將房間按照高度分組,第二級(jí)按照寬度分組。
Map<Integer,Map<Integer,List<Room>>> multistageMap = roomList.stream().collect(
Collectors.groupingBy(Room::getHigh,Collectors.groupingBy(Room::getWidth)));
System.out.println("multistageMap:"+JSON.toJSONString(multistageMap));
運(yùn)行結(jié)果:
{ "11": { "23": [ {"high": 11,"length": 56,"width": 23} ], "84": [ {"high": 11,"length": 48,"width": 84} ] }, "22": { "46": [ {"high": 22,"length": 112,"width": 46} ], "56": [ {"high": 22,"length": 75,"width": 56} ], "75": [ {"high": 22,"length": 62,"width": 75} ] }, "33": { "92": [ {"high": 33,"length": 224,"width": 92} ] } }
并行流
Stream的建立,使得并行計(jì)算變得容易,但是并行流在使用的時(shí)候也是需要注意的。
首先,必須是一個(gè)并行流,只要在終止方法執(zhí)行時(shí),流處于并行模式,那么所有的流操作就都會(huì)并行執(zhí)行。
Stream.of(roomList).parallel();
parallel方法可以將任意的串行流轉(zhuǎn)換為一個(gè)并行流。
其次要確保傳遞給并行流操作的函數(shù)是線程安全的。
int[] words = new int[23]; Stream.of(roomList).parallel().forEach(s->{ if(s.size()<10){ words[s.size()]++; } });
上面這個(gè)例子中的代碼就是錯(cuò)誤的,傳遞給并行流的操作并不是線程安全的。可以改為AtomicInteger的對(duì)象數(shù)組來作為計(jì)數(shù)器。
我們使在處理集合數(shù)據(jù)量較大的時(shí)候才能體現(xiàn)出并行流的優(yōu)勢(shì),并且目的是為了在保證線程安全的情況下,提升效率,利用多核CPU的資源。
小擴(kuò)展
使用Stream的API時(shí),在遍歷或處理流的過程中當(dāng)引用外部變量的時(shí)候會(huì)默認(rèn)的將變量當(dāng)成fianl變量來處理。所以有些同學(xué)就會(huì)覺得在遍歷的過程中取不出來集合的索引。其實(shí)可以換一種思想可以只遍歷集合索引,然后在遍歷中取值。
IntStream.range(0,roomList.size()).forEach(i->{
System.out.println(roomList.get(i));
});
作者:紀(jì)莫
歡迎任何形式的轉(zhuǎn)載,但請(qǐng)務(wù)必注明出處。
限于本人水平,如果文章和代碼有表述不當(dāng)之處,還請(qǐng)不吝賜教。
歡迎掃描二維碼關(guān)注公眾號(hào):Jimoer
文章會(huì)同步到公眾號(hào)上面,大家一起成長,共同提升技術(shù)能力。
聲援博主:如果您覺得文章對(duì)您有幫助,可以點(diǎn)擊文章右下角【推薦】一下。
您的鼓勵(lì)是博主的最大動(dòng)力!


浙公網(wǎng)安備 33010602011771號(hào)