? ? ? ?Stream 是對(duì)集合(Collection)對(duì)象功能的增強(qiáng)蔬芥,它專(zhuān)注于對(duì)集合對(duì)象進(jìn)行各種非常便利梆靖、高效的聚合操作,大批量數(shù)據(jù)操作笔诵。通常我們需要多行代碼才能完成的操作返吻,借助于Stream流式處理可以很簡(jiǎn)單的實(shí)現(xiàn)。
? ? ? ?Stream 不是集合元素乎婿,它不是數(shù)據(jù)結(jié)構(gòu)并不保存數(shù)據(jù)测僵,它是有關(guān)算法和計(jì)算的一個(gè)對(duì)象,它更像一個(gè)高級(jí)版本的Iterator次酌。同時(shí)Stream提供串行和并行兩種模式進(jìn)行匯聚操作恨课。比如你的Stream里面有很多數(shù)據(jù),Stream可以開(kāi)多個(gè)線程每個(gè)線程處理一部分岳服。最后把結(jié)果匯總起來(lái)剂公。
? ? ? ?在開(kāi)始之前我們先用一個(gè)圖來(lái)整體的概況下Stream。如下所示:
一 Stream流創(chuàng)建
? ? ? ?想使用Stream流吊宋,首先咱得先創(chuàng)建一個(gè)Stream流對(duì)象纲辽。創(chuàng)建Steam需要數(shù)據(jù)源.這些數(shù)據(jù)源可以是集合、可以是數(shù)組、可以使文件拖吼、甚至是你可以去自定義等等鳞上。
1.1 集合作為Stream數(shù)據(jù)源
? ? ? ?集合Collection作為Stream的數(shù)據(jù)源,應(yīng)該也是我們用的最多的一種數(shù)據(jù)源了吊档。Collection里面也提供了一些方法幫助我們把集合Collection轉(zhuǎn)換成Stream篙议。
1.1.1 stream()方法
? ? ? ?調(diào)用Collection.stream()函數(shù)創(chuàng)建一個(gè)Stream對(duì)象。相當(dāng)于把集合Collection里面的數(shù)據(jù)都導(dǎo)入到了Stream里面去了怠硼。
@Test
public void collectionStream() {
List<Integer> list = Lists.newArrayList(1, 2, 3, 4, 5);
// 使用List創(chuàng)建一個(gè)流對(duì)象
Stream<Integer> stream = list.stream();
// TODO: 對(duì)流對(duì)象做處理
}
1.1.2 parallelStream()方法
? ? ? ?調(diào)用Collection.parallelStream()創(chuàng)建Stream對(duì)象鬼贱。
@Test
public void collectionParallelStream() {
List<Integer> list = Lists.newArrayList(1, 2, 3, 4, 5);
// 使用List創(chuàng)建一個(gè)流對(duì)象
Stream<Integer> stream = list.parallelStream();
// TODO: 對(duì)流對(duì)象做處理
}
parallelStream()與stream()區(qū)別是parallelStream()使用多線程并發(fā)處理最后匯總結(jié)果,而stream()是單線程香璃。所以相對(duì)來(lái)說(shuō)parallelStream()效率要稍微高點(diǎn)这难。
1.2 數(shù)組作為Stream數(shù)據(jù)源
? ? ? ?數(shù)組也可以作為Stream的數(shù)據(jù)源。我們可以通過(guò)Arrays.stream()方法把一個(gè)數(shù)組轉(zhuǎn)化成流對(duì)象葡秒。Arrays.stream()方法很豐富姻乓,有很多個(gè)。大家可以根據(jù)實(shí)際情況使用眯牧。
@Test
public void arrayStream() {
int[] intArray = new int[10];
for (int index = 0; index < intArray.length; index++) {
intArray[index] = index;
}
// 使用數(shù)組創(chuàng)建一個(gè)流對(duì)象
IntStream stream = Arrays.stream(intArray);
// TODO: 對(duì)流對(duì)象做處理
}
1.3 BufferedReader作為Stream數(shù)據(jù)源
? ? ? ?我們也可以把BufferedReader里面lines方法把BufferedReader里面每一行的數(shù)據(jù)作為數(shù)據(jù)源生成一個(gè)Stream對(duì)象蹋岩。
@Test
public void bufferedReaderStream() {
File file = new File("/home/tuacy/github/google-guava-study/src/main/resources/application.yml");
try {
// 把文件里面的內(nèi)容一行一行的讀出來(lái)
BufferedReader in = new BufferedReader(new FileReader(file));
// 生成一個(gè)Stream對(duì)象
Stream<String> stream = in.lines();
// TODO: 對(duì)流對(duì)象做處理
} catch (IOException e) {
e.printStackTrace();
}
}
1.4 File作為Stream數(shù)據(jù)源
? ? ? ?Files里面多個(gè)生成Stream對(duì)象的方法,都是對(duì)Path(文件)的操作炸站。有的是指定Path目錄下所有的子文件(所有的子文件相當(dāng)于是一個(gè)列表了)作為Stream數(shù)據(jù)源星澳,有的把指定Path文件里面的每一行數(shù)據(jù)作為Stream的數(shù)據(jù)源。
1.4.1 Files.list()
? ? ? ?列出指定Path下面的所有文件旱易。把這些文件作為Stream數(shù)據(jù)源禁偎。
@Test
public void fileListStream() {
Path path = Paths.get("D:\\job\\git\\google-guava-study\\src\\main\\resources");
try {
// 找到指定path下的所有的文件
Stream<Path> stream = Files.list(path);
// TODO: 對(duì)流對(duì)象做處理
} catch (IOException e) {
e.printStackTrace();
}
}
1.4.2 Files.walk()
? ? ? ?Files.walk()方法用于遍歷子文件(包括文件夾)。參數(shù)maxDepth用于指定遍歷的深度阀坏。把子文件(子文件夾)作為Stream數(shù)據(jù)源如暖。
@Test
public void fileWalkStream() {
Path path = Paths.get("D:\\job\\git\\google-guava-study\\src\\main\\resources");
try {
// 第二個(gè)參數(shù)用于指定遍歷幾層
Stream<Path> stream = Files.walk(path, 2);
// TODO: 對(duì)流對(duì)象做處理
} catch (IOException e) {
e.printStackTrace();
}
}
1.4.3 Files.find()
? ? ? ?Files.find方法用于遍歷查找(過(guò)濾)子文件。參數(shù)里面會(huì)指定查詢(過(guò)濾)條件忌堂。把過(guò)濾出來(lái)的子文件作為Stream的數(shù)據(jù)源盒至。
@Test
public void fileFindStream() {
Path path = Paths.get("D:\\job\\git\\google-guava-study\\src\\main\\resources");
try {
// 找到指定path下的所有不是目錄的文件
Stream<Path> stream = Files.find(path, 2, (path1, basicFileAttributes) -> {
// 過(guò)濾掉目錄文件
return !basicFileAttributes.isDirectory();
});
// TODO: 對(duì)流對(duì)象做處理
} catch (IOException e) {
e.printStackTrace();
}
}
1.4.4 Files.lines()
? ? ? ?Files.lines方法是把指定Path文件里面的每一行內(nèi)容作為Stream的數(shù)據(jù)源。
@Test
public void fileLineStream() {
Path path = Paths.get("D:\\job\\git\\google-guava-study\\src\\main\\resources\\application.yml");
try {
// 生成一個(gè)Stream對(duì)象
Stream<String> stream = Files.lines(path);
// TODO: 對(duì)流對(duì)象做處理
} catch (IOException e) {
e.printStackTrace();
}
}
1.5 自己構(gòu)建Stream
? ? ? ?我們也可以自己去創(chuàng)建Stream自己提供數(shù)據(jù)源士修。Stream類(lèi)里面提供of()枷遂、iterate()、generate()棋嘲、builder()等一些方法來(lái)創(chuàng)建Stream酒唉,Stream的數(shù)據(jù)源我們自己提供。
1.5.1 Stream.of()
? ? ? ?Stream.of()函數(shù)參數(shù)就是數(shù)據(jù)源沸移。
Stream<Integer> ofSteam = Stream.of(1,2,3,4,5,6);
1.5.2 Stream.iterate()
? ? ? ?Stream.iterate()可以用來(lái)生成無(wú)限流痪伦,函數(shù)需要兩個(gè)參數(shù):第一個(gè)參數(shù)是初始值侄榴、第二個(gè)參數(shù)用于確定怎么根據(jù)前一個(gè)元素的值生成下一個(gè)元素。
// Stream.iterate() 流式迭代器 Stream.iterate()函數(shù)的第二個(gè)參數(shù)告訴你怎么去生成下一個(gè)元素
Stream<BigInteger> integers = Stream.iterate(
BigInteger.ONE,
new UnaryOperator<BigInteger>() {
@Override
public BigInteger apply(BigInteger bigInteger) {
return bigInteger.add(BigInteger.ONE);
}
});
// 簡(jiǎn)單輸出
integers.limit(10).forEach(new Consumer<BigInteger>() {
@Override
public void accept(BigInteger bigInteger) {
System.out.println(bigInteger.intValue());
}
});
1.5.3 Stream.generate()
? ? ? ?Stream.generate()也是用于生成一個(gè)無(wú)限流网沾。參數(shù)用于獲取每個(gè)元素癞蚕。
// Stream.generate() 生成無(wú)限流
Stream<Double> generateA = Stream.generate(new Supplier<Double>() {
@Override
public Double get() {
return java.lang.Math.random() * 100;
}
});
// 簡(jiǎn)單輸出前10個(gè)值
generateA.limit(10).forEach(new Consumer<Double>() {
@Override
public void accept(Double bigInteger) {
System.out.println(bigInteger.intValue());
}
});
1.5.4 Stream.build()
? ? ? ?Stream.build()通過(guò)建造者模式生成一個(gè)Stream建造器。然后把需要加入Stream里面的數(shù)據(jù)源一個(gè)一個(gè)通過(guò)建造器添加進(jìn)去辉哥。
// Stream.builder()構(gòu)造一個(gè)Stream對(duì)象
Stream.Builder<Integer> build = Stream.<Integer>builder().add(1)
.add(2)
.add(3);
build.accept(4);
build.accept(5);
build.build().forEach(new Consumer<Integer>() {
@Override
public void accept(Integer integer) {
System.out.println(integer);
}
});
// TODO: 對(duì)流對(duì)象做處理
1.6 其他Stream創(chuàng)建方式
? ? ? ?Stream其他創(chuàng)建方式我們就不一一舉例了桦山。有如下方式。
- Random.ints()证薇。
- BitSet.stream()度苔。
- Pattern.splitAsStream(java.lang.CharSequence)
- JarFile.stream()。
- ...
二 Stream流操作(中間操作符)
什么是操作符浑度?操作符就是對(duì)數(shù)據(jù)進(jìn)行的一種處理工作,一道加工程序鸦概;就好像工廠的工人對(duì)流水線上的產(chǎn)品進(jìn)行一道加工程序一樣箩张。
? ? ? ?Stream流操作就是對(duì)Stream流的各種處理。Stream里面已經(jīng)給提供了很多中間操作(我們一般稱之為操作符)窗市。
Stream提供的流操作符先慷。
Stream流操作符 | 解釋 |
---|---|
filter | 過(guò)濾 |
map | 對(duì)流里面每個(gè)元素做轉(zhuǎn)換 |
mapToInt | 把流里面的每個(gè)元素轉(zhuǎn)換成int |
mapToLong | 流里面每個(gè)元素轉(zhuǎn)換成long |
mapToDouble | 流里面每個(gè)元素轉(zhuǎn)換成double |
flatMap | 流里面每個(gè)元素轉(zhuǎn)換成Steam對(duì)象,最后平鋪成一個(gè)Stream對(duì)象 |
flatMapToInt | 流里面每個(gè)元素轉(zhuǎn)換成IntStream對(duì)象咨察,最后平鋪成一個(gè)IntStream對(duì)象 |
flatMapToLong | 流里面每個(gè)元素轉(zhuǎn)換成LongStream對(duì)象论熙,最后平鋪成一個(gè)LongStream對(duì)象 |
flatMapToDouble | 流里面每個(gè)元素轉(zhuǎn)換成DoubleStream對(duì)象,最后平鋪成一個(gè)DoubleStream對(duì)象 |
distinct | 去重 |
sorted | 排序 |
peek | 查看流里面的每個(gè)元素 |
limit | 返回前n個(gè)數(shù) |
skip | 跳過(guò)前n個(gè)元素 |
? ? ? ?Stream提供了這么多的操作符摄狱,而且這些操作符是可以組合起來(lái)使用脓诡。關(guān)于每個(gè)操作符的使用我們用一個(gè)簡(jiǎn)單的實(shí)例代碼來(lái)說(shuō)明。
2.1 filter
? ? ? ?filter用于對(duì)流里面的數(shù)據(jù)做過(guò)濾操作媒役。
// 過(guò)濾
@Test
public void filter() {
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// 過(guò)濾出偶數(shù)
Stream<Integer> filterStream = stream.filter(new Predicate<Integer>() {
@Override
public boolean test(Integer integer) {
return integer % 2 == 0;
}
});
// 簡(jiǎn)單輸出
filterStream.forEach(new Consumer<Integer>() {
@Override
public void accept(Integer integer) {
System.out.println(integer);
}
});
}
2.2 map
? ? ? ?map用于對(duì)流里面的元素做轉(zhuǎn)換祝谚。
// 轉(zhuǎn)換
@Test
public void map() {
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// 整數(shù)轉(zhuǎn)換為String
Stream<String> mapStream = stream.map(new Function<Integer, String>(){
@Override
public String apply(Integer integer) {
return String.valueOf(integer);
}
});
// 簡(jiǎn)單輸出
mapStream.forEach(new Consumer<String>() {
@Override
public void accept(String integer) {
System.out.println(integer);
}
});
}
2.3 mapToInt、mapToLong酣衷、mapToDouble
? ? ? ?mapToInt交惯、mapToLong、mapToDouble用于將流里面的元素轉(zhuǎn)換成對(duì)應(yīng)的類(lèi)型穿仪。
// 轉(zhuǎn)換
@Test
public void mapToInt() {
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// 整數(shù)轉(zhuǎn)換為String
IntStream mapStream = stream.mapToInt(new ToIntFunction<Integer>(){
@Override
public int applyAsInt(Integer value) {
return value == null ? 0 : value;
}
});
// 簡(jiǎn)單輸出總和
System.out.println(mapStream.sum());
}
2.4 flatMap席爽、flatMapToInt、flatMapToLong啊片、flatMapToDouble
? ? ? ?flatMap只锻、flatMapToInt、flatMapToLong钠龙、flatMapToDouble也是對(duì)每個(gè)元素的轉(zhuǎn)換炬藤,不過(guò)他們和map的不同點(diǎn)在于御铃,他們是吧每個(gè)元素轉(zhuǎn)換成一個(gè)Stream流,最終在平鋪成一個(gè)Stream流沈矿。
// 轉(zhuǎn)換
@Test
public void flatMap() {
Stream<String> stream = Stream.of("java:1", "android:2", "ios:3");
// 整數(shù)轉(zhuǎn)換為String
Stream<String> rerStream = stream.flatMap(
new Function<String, Stream<String>>() {
@Override
public Stream<String> apply(String s) {
// 分割
Iterable<String> iterableList = Splitter.on(':').trimResults() // 移除前面和后面的空白
.omitEmptyStrings()
.split(s);
return Lists.newArrayList(iterableList).parallelStream();
}
});
// 簡(jiǎn)單輸出
rerStream.forEach(new Consumer<String>() {
@Override
public void accept(String integer) {
System.out.println(integer);
}
});
}
// 轉(zhuǎn)換
@Test
public void reduce() {
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// 所有的元素相加上真,在加上20
Integer reduceValue = stream.reduce(20, new BinaryOperator<Integer>() {
@Override
public Integer apply(Integer integer, Integer integer2) {
System.out.println(integer);
System.out.println(integer2);
return integer + integer2;
}
});
System.out.println(reduceValue);
}
2.5 distinct
? ? ? ?distinct操作符用于去重。
// 去重
@Test
public void distinct() {
Stream<Integer> stream = Stream.of(1,2,3,1,2,3,1,2,3);
Stream<Integer> rerStream = stream.distinct();
// 簡(jiǎn)單輸出
rerStream.forEach(new Consumer<Integer>() {
@Override
public void accept(Integer integer) {
System.out.println(integer);
}
});
}
distinct根據(jù)某個(gè)字段去重羹膳。
2.6 sorted
? ? ? ?sorted操作符用于對(duì)流里面的元素排序睡互。
// 排序
@Test
public void sorted() {
Stream<Integer> stream = Stream.of(1,2,3,2,5,4,8,6);
Stream<Integer> rerStream = stream.sorted(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
if (o1.equals(o2)) {
return 0;
} else {
return o1 > o2 ? 1 : -1;
}
}
});
// 簡(jiǎn)單輸出
rerStream.forEach(new Consumer<Integer>() {
@Override
public void accept(Integer integer) {
System.out.println(integer);
}
});
}
2.7 peek
? ? ? ?peek操作符用于查看流里面的每個(gè)元素。在多個(gè)操作符同時(shí)使用的時(shí)候的中間加入peek操作符可以參考每個(gè)操作符之后的結(jié)果陵像。
// 查看
@Test
public void peek() {
Stream<Integer> stream = Stream.of(1,2,3,1,2,3,1,2,3);
// 查看
Stream<Integer> reStream = stream.peek(new Consumer<Integer>() {
@Override
public void accept(Integer integer) {
System.out.println(integer);
}
});
// 簡(jiǎn)單輸出
reStream.forEach(new Consumer<Integer>() {
@Override
public void accept(Integer integer) {
System.out.println(integer);
}
});
}
2.8 limit
? ? ? ?limit操作符用于取流前面多少個(gè)元素就珠。
// limit
@Test
public void limit() {
Stream<Integer> stream = Stream.of(1,2,3,1,2,3,1,2,3);
// limit
Stream<Integer> reStream = stream.limit(3);
// 簡(jiǎn)單輸出
reStream.forEach(new Consumer<Integer>() {
@Override
public void accept(Integer integer) {
System.out.println(integer);
}
});
}
2.9 skip
? ? ? ?skip用于跳過(guò)里面的多少個(gè)元素。
// skip
@Test
public void skip() {
Stream<Integer> stream = Stream.of(1,2,3,1,2,3,1,2,3);
// skip
Stream<Integer> reStream = stream.skip(3);
// 簡(jiǎn)單輸出
reStream.forEach(new Consumer<Integer>() {
@Override
public void accept(Integer integer) {
System.out.println(integer);
}
});
}
三. Stream流終端操作(終端操作符)
? ? ? ?Stream流終端操作是流式處理的最后一步醒颖,之前已經(jīng)對(duì)Stream做了一系列的處理之后妻怎。該拿出結(jié)果了。我們可以在終端操作中實(shí)現(xiàn)對(duì)流的遍歷泞歉、查找逼侦、歸約、收集等等一系列的操作腰耙。
Stream流終端操作提供的函數(shù)有
終端操作符 | 解釋 |
---|---|
forEach | 遍歷 |
forEachOrdered | 如果流里面的元素是有順序的則按順序遍歷 |
toArray | 轉(zhuǎn)換成數(shù)組 |
reduce | 歸約 - 根據(jù)一定的規(guī)則將Stream中的元素進(jìn)行計(jì)算后返回一個(gè)唯一的值 |
collect | 收集 - 對(duì)處理結(jié)果的封裝 |
min | 最小值 |
max | 最大值 |
count | 元素的個(gè)數(shù) |
anyMatch | 任何一個(gè)匹配到了就返回true |
allMatch | 所有都匹配上了就返回true |
noneMatch | 沒(méi)有一個(gè)匹配上就返回true |
findFirst | 返回滿足條件的第一個(gè)元素 |
findAny | 返回某個(gè)元素 |
? ? ? ?關(guān)于Stream終端操作部分榛丢,我們就著重講下collect()函數(shù)的使用。因?yàn)槠渌慕K端操作符都很好理解挺庞。collect()稍稍復(fù)雜一點(diǎn)晰赞。
3.1 collect()
? ? ? ?collect()的使用主要在于對(duì)參數(shù)的理解,所有我們這里要專(zhuān)門(mén)講下collect()函數(shù)的參數(shù)Collector這個(gè)類(lèi)选侨,以及怎么去構(gòu)建Collector對(duì)象掖鱼。只有在了解了這些之后,咱們才可以熟練的把他們用在各種場(chǎng)景中侵俗。
3.1.1 Collector
? ? ? ?Collector類(lèi)目前沒(méi)別的用處锨用,就是專(zhuān)門(mén)用來(lái)作為Stream的collect()方法的參數(shù)的。把Stream里面的數(shù)據(jù)轉(zhuǎn)換成我們最終想要的結(jié)果上隘谣。
Collector各個(gè)方法增拥,以及每個(gè)泛型的介紹
/**
* Collector是專(zhuān)門(mén)用來(lái)作為Stream的collect方法的參數(shù)的
*
* 泛型含義
* T:是流中要收集的對(duì)象的泛型
* A:是累加器的類(lèi)型,累加器是在收集過(guò)程中用于累積部分結(jié)果的對(duì)象寻歧。
* R:是收集操作得到的對(duì)象(通常但不一定是集合)的類(lèi)型掌栅。
*/
public interface Collector<T, A, R> {
/**
* 生成結(jié)果容器,容器類(lèi)型為A
* (多線程的情況下可能會(huì)調(diào)用多次,開(kāi)多個(gè)線程同時(shí)去處理一個(gè)流码泛,每個(gè)線程調(diào)用一次)
*/
Supplier<A> supplier();
/**
* A對(duì)應(yīng)supplier()函數(shù)創(chuàng)建的結(jié)果容器
* T對(duì)應(yīng)Stream流里面一個(gè)一個(gè)的元素
* 用于消費(fèi)元素猾封,也就是歸納元素,一般在這個(gè)里面把流里面的元素T(也可以轉(zhuǎn)換下)放到supplier()創(chuàng)建的結(jié)果T里面去
*/
BiConsumer<A, T> accumulator();
/**
* 用于兩個(gè)兩個(gè)合并并行執(zhí)行的線程的執(zhí)行結(jié)果噪珊,將其合并為一個(gè)最終結(jié)果A
* 多線程的情況下晌缘,多個(gè)線程并行執(zhí)行齐莲。每個(gè)線程產(chǎn)生一個(gè)結(jié)果
*/
BinaryOperator<A> combiner();
/**
* 用于將之前整合完的結(jié)果A轉(zhuǎn)換成為R
*
* combiner()完成之后了A, 這里還可以在轉(zhuǎn)一道。生成你自己想要的結(jié)果
*/
Function<A, R> finisher();
/**
* characteristics表示當(dāng)前Collector的特征值磷箕,
* 這是個(gè)不可變Set
* 它定義了收集器的行為--尤其是關(guān)于流是否可以多線程并行執(zhí)行选酗,以及可以使用哪些優(yōu)化的提示
*/
Set<Characteristics> characteristics();
/**
* 它定義了收集器的行為--尤其是關(guān)于流是否可以并行歸約,以及可以使用哪些優(yōu)化的提示
*/
enum Characteristics {
/**
* accumulator函數(shù)可以從多個(gè)線程同時(shí)調(diào)用岳枷,且該收集器可以并行歸約流披泪。如果收集器沒(méi)有標(biāo)為UNORDERED,
* 那它僅在用于無(wú)序數(shù)據(jù)源時(shí)才可以并行歸約
* 多線程并行
*/
CONCURRENT,
/**
* 歸約結(jié)果不受流中項(xiàng)目的遍歷和累積順序的影響(無(wú)序)
*/
UNORDERED,
/**
* 無(wú)需轉(zhuǎn)換結(jié)果
*/
IDENTITY_FINISH
}
/**
* 四參方法括袒,用于生成一個(gè)Collector,T代表流中的一個(gè)一個(gè)元素蚜锨,R代表最終的結(jié)果
*/
public static<T, R> Collector<T, R, R> of(Supplier<R> supplier,
BiConsumer<R, T> accumulator,
BinaryOperator<R> combiner,
Characteristics... characteristics);
/**
* 五參方法幔妨,用于生成一個(gè)Collector相恃,T代表流中的一個(gè)一個(gè)元素奋早,A代表中間結(jié)果夷蚊,R代表最終結(jié)果,finisher用于將A轉(zhuǎn)換為R
*/
public static<T, A, R> Collector<T, A, R> of(Supplier<A> supplier,
BiConsumer<A, T> accumulator,
BinaryOperator<A> combiner,
Function<A, R> finisher,
Characteristics... characteristics);
}
? ? ? ?有了上面的介紹饭于,接下來(lái)我們自己來(lái)new一個(gè)Collector對(duì)象蜀踏,把我們Steam流里面的數(shù)據(jù)轉(zhuǎn)換成List。(當(dāng)然了Collectors類(lèi)里面有提供這個(gè)方法掰吕,這里我們自己寫(xiě)一個(gè)也是為了方便大家的理解)
// 自己來(lái)組裝Collector,返回一個(gè)List
@Test
public void collectNew() {
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
List<Integer> intList = stream.collect(
new Collector<Integer, List<Integer>, List<Integer>>() {
// 生成結(jié)果容器颅痊,容器類(lèi)型為,我們這里為L(zhǎng)ist<Integer>
@Override
public Supplier<List<Integer>> supplier() {
return new Supplier<List<Integer>>() {
@Override
public List<Integer> get() {
return new ArrayList<>();
}
};
}
// 把流里面的結(jié)果都放到結(jié)果容器里面去
@Override
public BiConsumer<List<Integer>, Integer> accumulator() {
return new BiConsumer<List<Integer>, Integer>() {
@Override
public void accept(List<Integer> integers, Integer integer) {
integers.add(integer);
}
};
}
// 兩個(gè)兩個(gè)合并并行執(zhí)行的線程的執(zhí)行結(jié)果殖熟,將其合并為一個(gè)最終結(jié)果A
@Override
public BinaryOperator<List<Integer>> combiner() {
return new BinaryOperator<List<Integer>>() {
@Override
public List<Integer> apply(List<Integer> left, List<Integer> right) {
left.addAll(right);
return left;
}
};
}
// 可以對(duì)最終的結(jié)果做一個(gè)轉(zhuǎn)換操作
@Override
public Function<List<Integer>, List<Integer>> finisher() {
return new Function<List<Integer>, List<Integer>>() {
@Override
public List<Integer> apply(List<Integer> integers) {
return integers;
}
};
}
// 特征值
@Override
public Set<Characteristics> characteristics() {
return EnumSet.of(Collector.Characteristics.UNORDERED, Collector.Characteristics.IDENTITY_FINISH);
}
});
for (Integer item : intList) {
System.out.println(item);
}
}
3.1.2 Collectors
? ? ? ?Collectors是Collector的工具類(lèi),用來(lái)創(chuàng)建Collector對(duì)象斑响。它內(nèi)部已經(jīng)給我們提供了各種各樣的創(chuàng)建Collector對(duì)象的方法菱属,已經(jīng)能滿足我們大部分的需求了,我們可以直接拿來(lái)使用舰罚,非常方便纽门。
Collectors各個(gè)方法介紹
public final class Collectors {
/**
* 將流中的元素全部放置到一個(gè)集合中返回
*
* collectionFactory參數(shù)用于創(chuàng)建Collection對(duì)象(比如List,LinkeList等等)
*/
public static <T, C extends Collection<T>>
Collector<T, ?, C> toCollection(Supplier<C> collectionFactory);
/**
* 將流中的元素放置到一個(gè)列表集合中去。這個(gè)列表默認(rèn)為ArrayList
*/
public static <T>
Collector<T, ?, List<T>> toList();
/**
* 將流中的元素放置到一個(gè)無(wú)序集set中去营罢。默認(rèn)為HashSet
*/
public static <T>
Collector<T, ?, Set<T>> toSet();
/**
* joining的目的是將流中的元素全部以字符序列的方式連接到一起赏陵,可以指定連接符,甚至是結(jié)果的前后綴
*/
public static Collector<CharSequence, ?, String> joining();
public static Collector<CharSequence, ?, String> joining(CharSequence delimiter);
public static Collector<CharSequence, ?, String> joining(CharSequence delimiter,
CharSequence prefix,
CharSequence suffix);
/**
* 這個(gè)映射是首先對(duì)流中的每個(gè)元素進(jìn)行映射饲漾,即類(lèi)型轉(zhuǎn)換蝙搔,然后再將新元素以給定的Collector進(jìn)行歸納
*/
public static <T, U, A, R>
Collector<T, ?, R> mapping(Function<? super T, ? extends U> mapper,
Collector<? super U, A, R> downstream);
/**
* 該方法是在歸納動(dòng)作結(jié)束之后,對(duì)歸納的結(jié)果進(jìn)行再處理
*/
public static<T,A,R,RR> Collector<T,A,RR> collectingAndThen(Collector<T,A,R> downstream,
Function<R,RR> finisher);
/**
* 該方法用于計(jì)數(shù)
*/
public static <T> Collector<T, ?, Long> counting();
/**
* 生成一個(gè)用于獲取最小/最大值的Optional結(jié)果的Collector
*/
public static <T> Collector<T, ?, Optional<T>> minBy(Comparator<? super T> comparator);
public static <T> Collector<T, ?, Optional<T>> maxBy(Comparator<? super T> comparator);
/**
* 生成一個(gè)用于求元素和的Collector考传,首先通過(guò)給定的mapper將元素轉(zhuǎn)換類(lèi)型吃型,然后再求和
*/
public static <T> Collector<T, ?, Integer> summingInt(ToIntFunction<? super T> mapper);
public static <T> Collector<T, ?, Long> summingLong(ToLongFunction<? super T> mapper);
public static <T> Collector<T, ?, Double> summingDouble(ToDoubleFunction<? super T> mapper);
/**
* 生成一個(gè)用于求元素平均值的Collector,首選通過(guò)參數(shù)將元素轉(zhuǎn)換為指定的類(lèi)型
*/
public static <T> Collector<T, ?, Double> averagingInt(ToIntFunction<? super T> mapper);
public static <T> Collector<T, ?, Double> averagingLong(ToLongFunction<? super T> mapper);
public static <T> Collector<T, ?, Double> averagingDouble(ToDoubleFunction<? super T> mapper);
/**
* 規(guī)約 對(duì)流中的元素做統(tǒng)計(jì)歸納作用
* 和Stream類(lèi)里面的reducing操作符一樣
*/
public static <T> Collector<T, ?, T> reducing(T identity, BinaryOperator<T> op);
public static <T> Collector<T, ?, Optional<T>> reducing(BinaryOperator<T> op);
public static <T, U> Collector<T, ?, U> reducing(U identity,
Function<? super T, ? extends U> mapper,
BinaryOperator<U> op);
/**
* 這個(gè)方法是用于生成一個(gè)擁有分組功能的Collector
*/
public static <T, K> Collector<T, ?, Map<K, List<T>>>
groupingBy(Function<? super T, ? extends K> classifier);
public static <T, K, A, D>
Collector<T, ?, Map<K, D>> groupingBy(Function<? super T, ? extends K> classifier,
Collector<? super T, A, D> downstream);
public static <T, K, D, A, M extends Map<K, D>>
Collector<T, ?, M> groupingBy(Function<? super T, ? extends K> classifier,
Supplier<M> mapFactory,
Collector<? super T, A, D> downstream);
/**
* 和groupingBy方法一樣僚楞,只是返回的Collector是并行的
*/
public static <T, K>
Collector<T, ?, ConcurrentMap<K, List<T>>>
groupingByConcurrent(Function<? super T, ? extends K> classifier);
public static <T, K, A, D>
Collector<T, ?, ConcurrentMap<K, D>> groupingByConcurrent(Function<? super T, ? extends K> classifier,
Collector<? super T, A, D> downstream);
public static <T, K, A, D, M extends ConcurrentMap<K, D>>
Collector<T, ?, M> groupingByConcurrent(Function<? super T, ? extends K> classifier,
Supplier<M> mapFactory,
Collector<? super T, A, D> downstream);
/**
* 該方法將流中的元素按照給定的校驗(yàn)規(guī)則的結(jié)果分為兩個(gè)部分勤晚,放到一個(gè)map中返回枉层,map的鍵是Boolean類(lèi)型,值為元素的列表List赐写。
*/
public static <T>
Collector<T, ?, Map<Boolean, List<T>>> partitioningBy(Predicate<? super T> predicate);
public static <T, D, A>
Collector<T, ?, Map<Boolean, D>> partitioningBy(Predicate<? super T> predicate,
Collector<? super T, A, D> downstream);
/**
* 根據(jù)給定的鍵生成器和值生成器生成的鍵和值保存到一個(gè)map中返回鸟蜡,鍵和值的生成都依賴于元素,
* 可以指定出現(xiàn)重復(fù)鍵時(shí)的處理方案和保存結(jié)果的map
*/
public static <T, K, U>
Collector<T, ?, Map<K,U>> toMap(Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends U> valueMapper);
public static <T, K, U>
Collector<T, ?, Map<K,U>> toMap(Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends U> valueMapper,
BinaryOperator<U> mergeFunction);
public static <T, K, U, M extends Map<K, U>>
Collector<T, ?, M> toMap(Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends U> valueMapper,
BinaryOperator<U> mergeFunction,
Supplier<M> mapSupplier);
/**
* 并發(fā)版本的toMap
*/
public static <T, K, U>
Collector<T, ?, ConcurrentMap<K,U>> toConcurrentMap(Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends U> valueMapper);
public static <T, K, U>
Collector<T, ?, ConcurrentMap<K,U>>
toConcurrentMap(Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends U> valueMapper,
BinaryOperator<U> mergeFunction);
public static <T, K, U, M extends ConcurrentMap<K, U>>
Collector<T, ?, M> toConcurrentMap(Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends U> valueMapper,
BinaryOperator<U> mergeFunction,
Supplier<M> mapSupplier);
/**
* 這三個(gè)方法適用于匯總的血淌,返回值分別是IntSummaryStatistics矩欠,LongSummaryStatistics,DoubleSummaryStatistics
*/
public static <T>
Collector<T, ?, IntSummaryStatistics> summarizingInt(ToIntFunction<? super T> mapper);
public static <T>
Collector<T, ?, LongSummaryStatistics> summarizingLong(ToLongFunction<? super T> mapper);
public static <T>
Collector<T, ?, DoubleSummaryStatistics> summarizingDouble(ToDoubleFunction<? super T> mapper);
}
? ? ? ?為了加深大家的理解悠夯。接下來(lái)我們對(duì)Collectors里面的每個(gè)函數(shù)都寫(xiě)一個(gè)簡(jiǎn)單的實(shí)例癌淮。
3.1.2.1 toCollection
? ? ? ?將流中的元素全部放置到一個(gè)集合中返回,這里使用Collection沦补,泛指多種集合乳蓄。
@Test
public void toCollection() {
List<String> list = Arrays.asList("java", "ios", "c");
LinkedList<String> retList = list.stream().collect(Collectors.toCollection(
new Supplier<LinkedList<String>>() {
@Override
public LinkedList<String> get() {
return new LinkedList<>();
}
}));
}
3.1.2.2 toList
? ? ? ?將流中的元素放置到一個(gè)列表集合中去。這個(gè)列表默認(rèn)為ArrayList夕膀。
@Test
public void collectList() {
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
List<Integer> intList = stream.collect(Collectors.toList());
}
3.1.2.3 toSet
? ? ? ?將流中的元素放置到一個(gè)無(wú)序集set中去虚倒。默認(rèn)為HashSet。
@Test
public void toSet() {
List<String> list = Arrays.asList("java", "ios", "c");
Set<String> retList = list.stream().collect(Collectors.toSet());
}
3.1.2.4 joining
? ? ? ?joining的目的是將流中的元素全部以字符序列的方式連接到一起产舞,可以指定連接符魂奥,甚至是結(jié)果的前后綴。
@Test
public void joining() {
List<String> list = Arrays.asList("java", "ios", "c");
String ret = list.stream().collect(Collectors.joining(":", "@@", "@@"));
System.out.println(ret);//@@java:ios:c@@
}
3.1.2.5 mapping
? ? ? ?這個(gè)映射是首先對(duì)流中的每個(gè)元素進(jìn)行映射易猫,即類(lèi)型轉(zhuǎn)換耻煤,然后再將新元素以給定的Collector進(jìn)行歸納。
@Test
public void mapping() {
List<String> list = Arrays.asList("java", "ios", "c");
// 先把流里面的每個(gè)元素的前后加上[],之后在用:拼接起來(lái)
String ret = list.stream().collect(Collectors.mapping(
new Function<String, String>() {
@Override
public String apply(String s) {
return "[" + s + "]";
}
},
Collectors.joining(":")));
System.out.println(ret);//[java]:[ios]:[c]
}
3.1.2.6 collectingAndThen
? ? ? ?該方法是在歸納動(dòng)作結(jié)束之后准颓,對(duì)歸納的結(jié)果進(jìn)行再處理哈蝇。
@Test
public void collectingAndThen() {
List<String> list = Arrays.asList("java", "ios", "c");
// Collectors.toList()之后在把List<String>通過(guò)Joiner轉(zhuǎn)換String
String ret = list.stream().collect(Collectors.collectingAndThen(
Collectors.toList(),
new Function<List<String>, String>() {
@Override
public String apply(List<String> strings) {
return Joiner.on("; ")
.join(strings);
}
}));
System.out.println(ret);//java; ios; c
}
關(guān)于collectingAndThen有幾個(gè)在編碼過(guò)程經(jīng)常用到的場(chǎng)景,根據(jù)list中對(duì)象的屬性去重攘已。
@Test
public void collectingAndThen() {
List<Student> list = Arrays.asList(new Student("吳六", 26), new Student("張三", 26), new Student("李四", 27));
List<Student> ret = list.stream().collect(
Collectors.collectingAndThen(
Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(Student::getAge))), ArrayList::new
)
);
ret.forEach(new Consumer<Student>() {
@Override
public void accept(Student student) {
System.out.println(student.toString());
}
});
}
private static class Student {
private String name;
private int age;
public Student(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;
}
}
3.1.2.7 counting
? ? ? ?計(jì)算流里面的元素個(gè)數(shù)炮赦。
@Test
public void counting() {
List<String> list = Arrays.asList("java", "ios", "c");
// 元素個(gè)數(shù)
Long ret = list.stream().collect(Collectors.counting());
System.out.println(ret);//3
}
3.1.2.8 minBy/maxBy
? ? ? ?生成一個(gè)用于獲取最小/最大值的Optional結(jié)果的Collector。
@Test
public void minBy() {
List<String> list = Arrays.asList("java", "ios", "c");
// 這里簡(jiǎn)單的用字符串比較的大小
Optional<String> ret = list.stream().collect(Collectors.minBy(
new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o1.compareTo(o2);
}
}));
}
3.2.2.9 summingInt/summingLong/summingDouble
? ? ? ?生成一個(gè)用于求元素和的Collector样勃,首先通過(guò)給定的mapper將元素轉(zhuǎn)換類(lèi)型吠勘,然后再求和。
@Test
public void summingInt() {
List<Integer> list = Arrays.asList(1, 2, 3);
// 求和
Integer ret = list.stream().collect(Collectors.summingInt(
new ToIntFunction<Integer>() {
@Override
public int applyAsInt(Integer value) {
return value;
}
}));
}
3.2.2.10 averagingInt/averagingLong/averagingDouble
? ? ? ?生成一個(gè)用于求元素平均值的Collector彤灶,首選通過(guò)參數(shù)將元素轉(zhuǎn)換為指定的類(lèi)型看幼。
@Test
public void averagingInt() {
List<Integer> list = Arrays.asList(1, 2, 3);
// 求平均值
Double ret = list.stream().collect(Collectors.averagingInt(
new ToIntFunction<Integer>() {
@Override
public int applyAsInt(Integer value) {
return value;
}
}));
}
3.1.2.11 reducing
? ? ? ?reducing方法有三個(gè)重載方法,其實(shí)是和Stream里的三個(gè)reduce方法對(duì)應(yīng)的幌陕,二者是可以替換使用的诵姜,作用完全一致,也是對(duì)流中的元素做統(tǒng)計(jì)歸納作用(規(guī)約)。
@Test
public void reducing() {
List<Integer> list = Arrays.asList(1, 2, 3);
// 求平均值
Integer ret = list.stream().collect(Collectors.reducing(
10,
new Function<Integer, Integer>() {
@Override
public Integer apply(Integer integer) {
return integer * integer;
}
},
new BinaryOperator<Integer>() {
@Override
public Integer apply(Integer integer, Integer integer2) {
return integer + integer2;
}
}));
System.out.println(ret); // 10 + 1*1 + 2*2 + 3*3 = 24
}
3.1.2.12 groupingBy
? ? ? ?這個(gè)方法是用于生成一個(gè)擁有分組功能的Collector棚唆,它也有三個(gè)重載方法暇赤。這里要稍微解釋下參數(shù)最長(zhǎng)的函數(shù)每個(gè)參數(shù)的含義。
- Function<? super T, ? extends K> classifier:確定怎么從流里面每個(gè)元素獲取key宵凌。
- Supplier<M> mapFactory:用于創(chuàng)建最終要生成的對(duì)象一般都是Map鞋囊。
- Collector<? super T, A, D> downstream:每一組數(shù)據(jù)用什么來(lái)存放,一般是List瞎惫,所以這個(gè)參數(shù)一般是Collectors.toList()溜腐。
@Test
public void collectGroupingBy() {
List<Student> list = Arrays.asList(new Student("吳六", 26), new Student("張三", 26), new Student("李四", 27));
Map<Integer, List<Student>> ret = list.stream().collect(Collectors.groupingBy(
new Function<Student, Integer>() {
@Override
public Integer apply(Student student) {
return student.getAge();
}
},
new Supplier<Map<Integer, List<Student>>>() {
@Override
public Map<Integer, List<Student>> get() {
return new HashMap<>();
}
},
Collectors.toList()));
for (Map.Entry<Integer, List<Student>> entry : ret.entrySet()) {
Integer mapKey = entry.getKey();
List<Student> mapValue = entry.getValue();
System.out.println(mapKey + ":" + mapValue);
}
}
private static class Student {
private String name;
private int age;
public Student(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;
}
}
groupingByConcurrent和groupingBy是一樣的,只不過(guò)groupingByConcurrent生成的是ConcurrentMap瓜喇,而groupingBy生成的是Map挺益。
3.1.2.13 partitioningBy
? ? ? ?該方法將流中的元素按照給定的校驗(yàn)規(guī)則的結(jié)果分為兩個(gè)部分,放到一個(gè)map中返回乘寒,map的鍵是Boolean類(lèi)型望众,值為元素的列表List。
@Test
public void partitioningBy() {
List<Integer> list = Arrays.asList(1, 2, 3);
// 求平均值
Map<Boolean, List<Integer>> ret = list.stream()
.collect(Collectors.partitioningBy(
new Predicate<Integer>() {
@Override
public boolean test(Integer integer) {
return integer % 2 == 0;
}
},
Collectors.toList()));
for (Map.Entry<Boolean, List<Integer>> entry : ret.entrySet()) {
Boolean mapKey = entry.getKey();
List<Integer> mapValue = entry.getValue();
System.out.println(mapKey + ":" + mapValue);
}
}
3.1.2.14 toMap
? ? ? ?toMap方法是根據(jù)給定的鍵生成器和值生成器生成的鍵和值保存到一個(gè)map中返回伞辛,鍵和值的生成都依賴于元素烂翰,可以指定出現(xiàn)重復(fù)鍵時(shí)的處理方案和保存結(jié)果的map。
@Test
public void toMap() {
List<Student> list = Arrays.asList(new Student("吳六", 26), new Student("張三", 26), new Student("李四", 27));
Map<Integer, List<Student>> ret = list.stream()
.collect(Collectors.toMap(
// key
new Function<Student, Integer>() {
@Override
public Integer apply(Student student) {
return student.getAge();
}
},
// value
new Function<Student, List<Student>>() {
@Override
public List<Student> apply(Student student) {
return Lists.newArrayList(student);
}
},
// 有系統(tǒng)key的value怎么處理
new BinaryOperator<List<Student>>() {
@Override
public List<Student> apply(List<Student> students, List<Student> students2) {
students.addAll(students2);
return students;
}
}));
for (Map.Entry<Integer, List<Student>> entry : ret.entrySet()) {
Integer mapKey = entry.getKey();
List<Student> mapValue = entry.getValue();
System.out.println(mapKey + ":" + mapValue);
}
}
private static class Student {
private String name;
private int age;
public Student(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;
}
}
3.1.2.15 summarizingInt/summarizingLong/summarizingDouble
? ? ? ?這三個(gè)方法適用于匯總的蚤氏,返回值分別是IntSummaryStatistics甘耿,LongSummaryStatistics,DoubleSummaryStatistics竿滨。
@Test
public void summarizingInt() {
List<Integer> list = Arrays.asList(1, 2, 3);
IntSummaryStatistics ret = list.stream()
.collect(Collectors.summarizingInt(
new ToIntFunction<Integer>(){
@Override
public int applyAsInt(Integer value) {
return value;
}
}));
System.out.println(ret.getAverage());
}