青岛主流网站/河南自助建站seo公司
Stream 是对集合(Collection)对象功能的增强,它专注于对集合对象进行各种非常便利、高效的聚合操作,或者大批量数据操作。通常我们需要多行代码才能完成的操作,借助于Stream流式处理可以很简单的实现。
Stream 不是集合元素,它不是数据结构并不保存数据,它是有关算法和计算的,它更像一个高级版本的Iterator。同时Stream提供串行和并行两种模式进行汇聚操作。比如你的Stream里面有很多数据,Stream可以开多个线程每个线程处理一部分。最后把结果汇总起来。
在开始之前我们先用一个图来整体的概况下Stream。如下所示:
一 Stream流创建
想使用Stream流,首先咱得先创建一个Stream流对象。创建Steam需要数据源.这些数据源可以是集合、可以是数组、可以使文件、甚至是你可以去自定义等等。
1.1 集合作为Stream数据源
集合Collection作为Stream的数据源,应该也是我们用的最多的一种数据源了。Collection里面也提供了一些方法帮助我们把集合Collection转换成Stream。
1.1.1 stream()方法
调用Collection.stream()函数创建一个Stream对象。相当于把集合Collection里面的数据都导入到了Stream里面去了。
@Testpublic void collectionStream() {List<Integer> list = Lists.newArrayList(1, 2, 3, 4, 5);// 使用List创建一个流对象Stream<Integer> stream = list.stream();// TODO: 对流对象做处理}
1.1.2 parallelStream()方法
调用Collection.parallelStream()创建Stream对象。
@Testpublic void collectionParallelStream() {List<Integer> list = Lists.newArrayList(1, 2, 3, 4, 5);// 使用List创建一个流对象Stream<Integer> stream = list.parallelStream();// TODO: 对流对象做处理}
parallelStream()与stream()区别是parallelStream()使用多线程并发处理最后子啊汇总结果,而stream()是单线程。所以相对来说parallelStream()效率要稍微高点。
1.2 数组作为Stream数据源
数组也可以作为Stream的数据源。我们可以通过Arrays.stream()方法把一个数组转化成流对象。Arrays.stream()方法很丰富,有很多个。大家可以根据实际情况使用。
@Testpublic void arrayStream() {int[] intArray = new int[10];for (int index = 0; index < intArray.length; index++) {intArray[index] = index;}// 使用数组创建一个流对象IntStream stream = Arrays.stream(intArray);// TODO: 对流对象做处理}
1.3 BufferedReader作为Stream数据源
我们也可以把BufferedReader里面lines方法把BufferedReader里面每一行的数据作为数据源生成一个Stream对象。
@Testpublic void bufferedReaderStream() {File file = new File("/home/tuacy/github/google-guava-study/src/main/resources/application.yml");try {// 把文件里面的内容一行一行的读出来BufferedReader in = new BufferedReader(new FileReader(file));// 生成一个Stream对象Stream<String> stream = in.lines();// TODO: 对流对象做处理} catch (IOException e) {e.printStackTrace();}}
1.4 File作为Stream数据源
Files里面多个生成Stream对象的方法,都是对Path(文件)的操作。有的是指定Path目录下所有的子文件(所有的子文件相当于是一个列表了)作为Stream数据源,有的把指定Path文件里面的每一行数据作为Stream的数据源。
1.4.1 Files.list()
列出指定Path下面的所有文件。把这些文件作为Stream数据源。
@Testpublic void fileListStream() {Path path = Paths.get("D:\\job\\git\\google-guava-study\\src\\main\\resources");try {// 找到指定path下的所有的文件Stream<Path> stream = Files.list(path);// TODO: 对流对象做处理} catch (IOException e) {e.printStackTrace();}}
1.4.2 Files.walk()
Files.walk()方法用于遍历子文件(包括文件夹)。参数maxDepth用于指定遍历的深度。把子文件(子文件夹)作为Stream数据源。
@Testpublic void fileWalkStream() {Path path = Paths.get("D:\\job\\git\\google-guava-study\\src\\main\\resources");try {// 第二个参数用于指定遍历几层Stream<Path> stream = Files.walk(path, 2);// TODO: 对流对象做处理} catch (IOException e) {e.printStackTrace();}}
1.4.3 Files.find()
Files.find方法用于遍历查找(过滤)子文件。参数里面会指定查询(过滤)条件。把过滤出来的子文件作为Stream的数据源。
@Testpublic 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) -> {// 过滤掉目录文件return !basicFileAttributes.isDirectory();});// TODO: 对流对象做处理} catch (IOException e) {e.printStackTrace();}}
1.4.4 Files.lines()
Files.lines方法是把指定Path文件里面的每一行内容作为Stream的数据源。
@Testpublic void fileLineStream() {Path path = Paths.get("D:\\job\\git\\google-guava-study\\src\\main\\resources\\application.yml");try {// 生成一个Stream对象Stream<String> stream = Files.lines(path);// TODO: 对流对象做处理} catch (IOException e) {e.printStackTrace();}}
1.5 自己构建Stream
我们也可以自己去创建Stream自己提供数据源。Stream类里面提供of()、iterate()、generate()、builder()等一些方法来创建Stream,Stream的数据源我们自己提供。
1.5.1 Stream.of()
Stream.of()函数参数就是数据源。
Stream<Integer> ofSteam = Stream.of(1,2,3,4,5,6);
1.5.2 Stream.iterate()
Stream.iterate()可以用来生成无限流,函数需要两个参数:第一个参数是初始值、第二个参数用于确定怎么根据前一个元素的值生成下一个元素。
// Stream.iterate() 流式迭代器 Stream.iterate()函数的第二个参数告诉你怎么去生成下一个元素Stream<BigInteger> integers = Stream.iterate(BigInteger.ONE,new UnaryOperator<BigInteger>() {@Overridepublic BigInteger apply(BigInteger bigInteger) {return bigInteger.add(BigInteger.ONE);}});// 简单输出integers.limit(10).forEach(new Consumer<BigInteger>() {@Overridepublic void accept(BigInteger bigInteger) {System.out.println(bigInteger.intValue());}});
1.5.3 Stream.generate()
Stream.generate()也是用于生成一个无限流。参数用于获取每个元素。
// Stream.generate() 生成无限流Stream<Double> generateA = Stream.generate(new Supplier<Double>() {@Overridepublic Double get() {return java.lang.Math.random() * 100;}});// 简单输出前10个值generateA.limit(10).forEach(new Consumer<Double>() {@Overridepublic void accept(Double bigInteger) {System.out.println(bigInteger.intValue());}});
1.5.4 Stream.build()
Stream.build()通过建造者模式生成一个Stream建造器。然后把需要加入Stream里面的数据源一个一个通过建造器添加进去。
// Stream.builder()构造一个Stream对象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>() {@Overridepublic void accept(Integer integer) {System.out.println(integer);}});// TODO: 对流对象做处理
1.6 其他Stream创建方式
Stream其他创建方式我们就不一一举例了。有如下方式。
- Random.ints()。
- BitSet.stream()。
- Pattern.splitAsStream(java.lang.CharSequence)
- JarFile.stream()。
- …
二 Stream流操作(操作符)
Stream流操作就是对Stream流的各种处理。Stream里面已经给提供了很多中间操作(我们一般称之为操作符)。
Stream提供的流操作符。
Stream流操作符 | 解释 |
---|---|
filter | 过滤 |
map | 对流里面每个元素做转换 |
mapToInt | 把流里面的每个元素转换成int |
mapToLong | 流里面每个元素转换成long |
mapToDouble | 流里面每个元素转换成double |
flatMap | 流里面每个元素转换成Steam对象,最后平铺成一个Stream对象 |
flatMapToInt | 流里面每个元素转换成IntStream对象,最后平铺成一个IntStream对象 |
flatMapToLong | 流里面每个元素转换成LongStream对象,最后平铺成一个LongStream对象 |
flatMapToDouble | 流里面每个元素转换成DoubleStream对象,最后平铺成一个DoubleStream对象 |
distinct | 去重 |
sorted | 排序 |
peek | 查看流里面的每个元素 |
limit | 返回前n个数 |
skip | 跳过前n个元素 |
Stream提供了这么多的操作符,而且这些操作符是可以组合起来使用。关于每个操作符的使用我们用一个简单的实例代码来说明。
2.1 filter
filter用于对流里面的数据做过滤操作。
// 过滤@Testpublic void filter() {Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);// 过滤出偶数Stream<Integer> filterStream = stream.filter(new Predicate<Integer>() {@Overridepublic boolean test(Integer integer) {return integer % 2 == 0;}});// 简单输出filterStream.forEach(new Consumer<Integer>() {@Overridepublic void accept(Integer integer) {System.out.println(integer);}});}
2.2 map
map用于对流里面的元素做转换。
// 转换@Testpublic void map() {Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);// 整数转换为StringStream<String> mapStream = stream.map(new Function<Integer, String>(){@Overridepublic String apply(Integer integer) {return String.valueOf(integer);}});// 简单输出mapStream.forEach(new Consumer<String>() {@Overridepublic void accept(String integer) {System.out.println(integer);}});}
2.3 mapToInt、mapToLong、mapToDouble
mapToInt、mapToLong、mapToDouble用于将流里面的元素转换成对应的类型。
// 转换@Testpublic void mapToInt() {Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);// 整数转换为StringIntStream mapStream = stream.mapToInt(new ToIntFunction<Integer>(){@Overridepublic int applyAsInt(Integer value) {return value == null ? 0 : value;}});// 简单输出总和System.out.println(mapStream.sum());}
2.4 flatMap、flatMapToInt、flatMapToLong、flatMapToDouble
flatMap、flatMapToInt、flatMapToLong、flatMapToDouble也是对每个元素的转换,不过他们和map的不同点在于,他们是吧每个元素转换成一个Stream流,最终在平铺成一个Stream流。
// 转换@Testpublic void flatMap() {Stream<String> stream = Stream.of("java:1", "android:2", "ios:3");// 整数转换为StringStream<String> rerStream = stream.flatMap(new Function<String, Stream<String>>() {@Overridepublic Stream<String> apply(String s) {// 分割Iterable<String> iterableList = Splitter.on(':').trimResults() // 移除前面和后面的空白.omitEmptyStrings().split(s);return Lists.newArrayList(iterableList).parallelStream();}});// 简单输出rerStream.forEach(new Consumer<String>() {@Overridepublic void accept(String integer) {System.out.println(integer);}});}
// 转换@Testpublic void reduce() {Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);// 所有的元素相加,在加上20Integer reduceValue = stream.reduce(20, new BinaryOperator<Integer>() {@Overridepublic 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操作符用于去重。
// 去重@Testpublic void distinct() {Stream<Integer> stream = Stream.of(1,2,3,1,2,3,1,2,3);Stream<Integer> rerStream = stream.distinct();// 简单输出rerStream.forEach(new Consumer<Integer>() {@Overridepublic void accept(Integer integer) {System.out.println(integer);}});}
2.6 sorted
sorted操作符用于对流里面的元素排序。
// 排序@Testpublic void sorted() {Stream<Integer> stream = Stream.of(1,2,3,2,5,4,8,6);Stream<Integer> rerStream = stream.sorted(new Comparator<Integer>() {@Overridepublic int compare(Integer o1, Integer o2) {if (o1.equals(o2)) {return 0;} else {return o1 > o2 ? 1 : -1;}}});// 简单输出rerStream.forEach(new Consumer<Integer>() {@Overridepublic void accept(Integer integer) {System.out.println(integer);}});}
2.7 peek
peek操作符用于查看流里面的每个元素。在多个操作符同时使用的时候的中间加入peek操作符可以参考每个操作符之后的结果。
// 查看@Testpublic void peek() {Stream<Integer> stream = Stream.of(1,2,3,1,2,3,1,2,3);// 查看Stream<Integer> reStream = stream.peek(new Consumer<Integer>() {@Overridepublic void accept(Integer integer) {System.out.println(integer);}});// 简单输出reStream.forEach(new Consumer<Integer>() {@Overridepublic void accept(Integer integer) {System.out.println(integer);}});}
2.8 limit
limit操作符用于取流前面多少个元素。
// limit@Testpublic void limit() {Stream<Integer> stream = Stream.of(1,2,3,1,2,3,1,2,3);// limitStream<Integer> reStream = stream.limit(3);// 简单输出reStream.forEach(new Consumer<Integer>() {@Overridepublic void accept(Integer integer) {System.out.println(integer);}});}
2.9 skip
skip用于跳过里面的多少个元素。
// skip@Testpublic void skip() {Stream<Integer> stream = Stream.of(1,2,3,1,2,3,1,2,3);// skipStream<Integer> reStream = stream.skip(3);// 简单输出reStream.forEach(new Consumer<Integer>() {@Overridepublic void accept(Integer integer) {System.out.println(integer);}});}
三. Stream流终端操作
Stream流终端操作是流式处理的最后一步,之前已经对Stream做了一系列的处理之后。该拿出结果了。我们可以在终端操作中实现对流的遍历、查找、归约、收集等等一系列的操作。
Stream流终端操作提供的函数有
终端操作符 | 解释 |
---|---|
forEach | 遍历 |
forEachOrdered | 如果流里面的元素是有顺序的则按顺序遍历 |
toArray | 转换成数组 |
reduce | 归约 - 根据一定的规则将Stream中的元素进行计算后返回一个唯一的值 |
collect | 收集 - 对处理结果的封装 |
min | 最小值 |
max | 最大值 |
count | 元素的个数 |
anyMatch | 任何一个匹配到了就返回true |
allMatch | 所有都匹配上了就返回true |
noneMatch | 没有一个匹配上就返回true |
findFirst | 返回满足条件的第一个元素 |
findAny | 返回某个元素 |
关于Stream终端操作部分,我们就着重讲下collect()函数的使用。因为其他的终端操作符都很好理解。collect()稍稍复杂一点。
3.1 collect()
collect()的使用主要在于对参数的理解,所有我们这里要专门讲下collect()函数的参数Collector这个类,以及怎么去构建Collector对象。只有在了解了这些之后,咱们才可以熟练的把他们用在各种场景中。
3.1.1 Collector
Collector类目前没别的用处,就是专门用来作为Stream的collect()方法的参数的。把Stream里面的数据转换成我们最终想要的结果上。
Collector各个方法,以及每个泛型的介绍,都在下面详细的解释了。如果有疑问可以留言。
/*** Collector是专门用来作为Stream的collect方法的参数的** 泛型含义* T:是流中要收集的对象的泛型* A:是累加器的类型,累加器是在收集过程中用于累积部分结果的对象。* R:是收集操作得到的对象(通常但不一定是集合)的类型。*/
public interface Collector<T, A, R> {/*** 生成结果容器,容器类型为A* (多线程的情况下可能会调用多次,开多个线程同时去处理一个流,每个线程调用一次)*/Supplier<A> supplier();/*** A对应supplier()函数创建的结果容器* T对应Stream流里面一个一个的元素* 用于消费元素,也就是归纳元素,一般在这个里面把流里面的元素T(也可以转换下)放到supplier()创建的结果T里面去*/BiConsumer<A, T> accumulator();/*** 用于两个两个合并并行执行的线程的执行结果,将其合并为一个最终结果A* 多线程的情况下,多个线程并行执行。每个线程产生一个结果*/BinaryOperator<A> combiner();/*** 用于将之前整合完的结果A转换成为R** combiner()完成之后了A, 这里还可以在转一道。生成你自己想要的结果*/Function<A, R> finisher();/*** characteristics表示当前Collector的特征值,* 这是个不可变Set* 它定义了收集器的行为--尤其是关于流是否可以多线程并行执行,以及可以使用哪些优化的提示*/Set<Characteristics> characteristics();/*** 它定义了收集器的行为--尤其是关于流是否可以并行归约,以及可以使用哪些优化的提示*/enum Characteristics {/*** accumulator函数可以从多个线程同时调用,且该收集器可以并行归约流。如果收集器没有标为UNORDERED,* 那它仅在用于无序数据源时才可以并行归约* 多线程并行*/CONCURRENT,/*** 归约结果不受流中项目的遍历和累积顺序的影响(无序)*/UNORDERED,/*** 无需转换结果*/IDENTITY_FINISH}/*** 四参方法,用于生成一个Collector,T代表流中的一个一个元素,R代表最终的结果*/public static<T, R> Collector<T, R, R> of(Supplier<R> supplier,BiConsumer<R, T> accumulator,BinaryOperator<R> combiner,Characteristics... characteristics);/*** 五参方法,用于生成一个Collector,T代表流中的一个一个元素,A代表中间结果,R代表最终结果,finisher用于将A转换为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);}
有了上面的介绍,接下来我们自己来new一个Collector对象,把我们Steam流里面的数据转换成List。(当然了Collectors类里面有提供这个方法,这里我们自己写一个也是为了方便大家的理解)
// 自己来组装Collector,返回一个List@Testpublic 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>>() {// 生成结果容器,容器类型为,我们这里为List<Integer>@Overridepublic Supplier<List<Integer>> supplier() {return new Supplier<List<Integer>>() {@Overridepublic List<Integer> get() {return new ArrayList<>();}};}// 把流里面的结果都放到结果容器里面去@Overridepublic BiConsumer<List<Integer>, Integer> accumulator() {return new BiConsumer<List<Integer>, Integer>() {@Overridepublic void accept(List<Integer> integers, Integer integer) {integers.add(integer);}};}// 两个两个合并并行执行的线程的执行结果,将其合并为一个最终结果A@Overridepublic BinaryOperator<List<Integer>> combiner() {return new BinaryOperator<List<Integer>>() {@Overridepublic List<Integer> apply(List<Integer> left, List<Integer> right) {left.addAll(right);return left;}};}// 可以对最终的结果做一个转换操作@Overridepublic Function<List<Integer>, List<Integer>> finisher() {return new Function<List<Integer>, List<Integer>>() {@Overridepublic List<Integer> apply(List<Integer> integers) {return integers;}};}// 特征值@Overridepublic 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的工具类,用来创建Collector对象。它内部以及给我们提供了各种各样的创建Collector对象的方法,已经能满足我们大部分的需求了,我们可以直接拿来使用,非常方便。
Collectors各个方法介绍
public final class Collectors {/*** 将流中的元素全部放置到一个集合中返回** collectionFactory参数用于创建Collection对象(比如List,LinkeList等等)*/public static <T, C extends Collection<T>>Collector<T, ?, C> toCollection(Supplier<C> collectionFactory);/*** 将流中的元素放置到一个列表集合中去。这个列表默认为ArrayList*/public static <T>Collector<T, ?, List<T>> toList();/*** 将流中的元素放置到一个无序集set中去。默认为HashSet*/public static <T>Collector<T, ?, Set<T>> toSet();/*** joining的目的是将流中的元素全部以字符序列的方式连接到一起,可以指定连接符,甚至是结果的前后缀*/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);/*** 这个映射是首先对流中的每个元素进行映射,即类型转换,然后再将新元素以给定的Collector进行归纳*/public static <T, U, A, R>Collector<T, ?, R> mapping(Function<? super T, ? extends U> mapper,Collector<? super U, A, R> downstream);/*** 该方法是在归纳动作结束之后,对归纳的结果进行再处理*/public static<T,A,R,RR> Collector<T,A,RR> collectingAndThen(Collector<T,A,R> downstream,Function<R,RR> finisher);/*** 该方法用于计数*/public static <T> Collector<T, ?, Long> counting();/*** 生成一个用于获取最小/最大值的Optional结果的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);/*** 生成一个用于求元素和的Collector,首先通过给定的mapper将元素转换类型,然后再求和*/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);/*** 生成一个用于求元素平均值的Collector,首选通过参数将元素转换为指定的类型*/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);/*** 规约 对流中的元素做统计归纳作用* 和Stream类里面的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);/*** 这个方法是用于生成一个拥有分组功能的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);/*** 该方法将流中的元素按照给定的校验规则的结果分为两个部分,放到一个map中返回,map的键是Boolean类型,值为元素的列表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);/*** 根据给定的键生成器和值生成器生成的键和值保存到一个map中返回,键和值的生成都依赖于元素,* 可以指定出现重复键时的处理方案和保存结果的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);/*** 并发版本的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);/*** 这三个方法适用于汇总的,返回值分别是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);
}
为了加深大家的理解。接下来我们对Collectors里面的每个函数都写一个简单的实例。
3.1.2.1 toCollection
将流中的元素全部放置到一个集合中返回,这里使用Collection,泛指多种集合。
@Testpublic void toCollection() {List<String> list = Arrays.asList("java", "ios", "c");LinkedList<String> retList = list.stream().collect(Collectors.toCollection(new Supplier<LinkedList<String>>() {@Overridepublic LinkedList<String> get() {return new LinkedList<>();}}));}
3.1.2.2 toList
将流中的元素放置到一个列表集合中去。这个列表默认为ArrayList。
@Testpublic 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
将流中的元素放置到一个无序集set中去。默认为HashSet。
@Testpublic void toSet() {List<String> list = Arrays.asList("java", "ios", "c");Set<String> retList = list.stream().collect(Collectors.toSet());}
3.1.2.4 joining
joining的目的是将流中的元素全部以字符序列的方式连接到一起,可以指定连接符,甚至是结果的前后缀。
@Testpublic 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
这个映射是首先对流中的每个元素进行映射,即类型转换,然后再将新元素以给定的Collector进行归纳。
@Testpublic void mapping() {List<String> list = Arrays.asList("java", "ios", "c");// 先把流里面的每个元素的前后加上[],之后在用:拼接起来String ret = list.stream().collect(Collectors.mapping(new Function<String, String>() {@Overridepublic String apply(String s) {return "[" + s + "]";}},Collectors.joining(":")));System.out.println(ret);//[java]:[ios]:[c]}
3.1.2.6 collectingAndThen
该方法是在归纳动作结束之后,对归纳的结果进行再处理。
@Testpublic void collectingAndThen() {List<String> list = Arrays.asList("java", "ios", "c");// Collectors.toList()之后在把List<String>通过Joiner转换StringString ret = list.stream().collect(Collectors.collectingAndThen(Collectors.toList(),new Function<List<String>, String>() {@Overridepublic String apply(List<String> strings) {return Joiner.on("; ").join(strings);}}));System.out.println(ret);//java; ios; c}
3.1.2.7 counting
计算流里面的元素个数。
@Testpublic void counting() {List<String> list = Arrays.asList("java", "ios", "c");// 元素个数Long ret = list.stream().collect(Collectors.counting());System.out.println(ret);//3}
3.1.2.8 minBy/maxBy
生成一个用于获取最小/最大值的Optional结果的Collector。
@Testpublic void minBy() {List<String> list = Arrays.asList("java", "ios", "c");// 这里简单的用字符串比较的大小Optional<String> ret = list.stream().collect(Collectors.minBy(new Comparator<String>() {@Overridepublic int compare(String o1, String o2) {return o1.compareTo(o2);}}));}
3.2.2.9 summingInt/summingLong/summingDouble
生成一个用于求元素和的Collector,首先通过给定的mapper将元素转换类型,然后再求和。
@Testpublic void summingInt() {List<Integer> list = Arrays.asList(1, 2, 3);// 求和Integer ret = list.stream().collect(Collectors.summingInt(new ToIntFunction<Integer>() {@Overridepublic int applyAsInt(Integer value) {return value;}}));}
3.2.2.10 averagingInt/averagingLong/averagingDouble
生成一个用于求元素平均值的Collector,首选通过参数将元素转换为指定的类型。
@Testpublic void averagingInt() {List<Integer> list = Arrays.asList(1, 2, 3);// 求平均值Double ret = list.stream().collect(Collectors.averagingInt(new ToIntFunction<Integer>() {@Overridepublic int applyAsInt(Integer value) {return value;}}));}
3.1.2.11 reducing
reducing方法有三个重载方法,其实是和Stream里的三个reduce方法对应的,二者是可以替换使用的,作用完全一致,也是对流中的元素做统计归纳作用(规约)。
@Testpublic void reducing() {List<Integer> list = Arrays.asList(1, 2, 3);// 求平均值Integer ret = list.stream().collect(Collectors.reducing(10,new Function<Integer, Integer>() {@Overridepublic Integer apply(Integer integer) {return integer * integer;}},new BinaryOperator<Integer>() {@Overridepublic 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
这个方法是用于生成一个拥有分组功能的Collector,它也有三个重载方法。这里要稍微解释下参数最长的函数每个参数的含义。
- Function<? super T, ? extends K> classifier:确定怎么从流里面每个元素获取key。
- Supplier mapFactory:用于创建最终要生成的对象一般都是Map。
- Collector<? super T, A, D> downstream:每一组数据用什么来存放,一般是List,所以这个参数一般是Collectors.toList()。
@Testpublic 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>() {@Overridepublic Integer apply(Student student) {return student.getAge();}},new Supplier<Map<Integer, List<Student>>>() {@Overridepublic 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是一样的,只不过groupingByConcurrent生成的是ConcurrentMap,而groupingBy生成的是Map。
3.1.2.13 partitioningBy
该方法将流中的元素按照给定的校验规则的结果分为两个部分,放到一个map中返回,map的键是Boolean类型,值为元素的列表List。
@Testpublic void partitioningBy() {List<Integer> list = Arrays.asList(1, 2, 3);// 求平均值Map<Boolean, List<Integer>> ret = list.stream().collect(Collectors.partitioningBy(new Predicate<Integer>() {@Overridepublic 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方法是根据给定的键生成器和值生成器生成的键和值保存到一个map中返回,键和值的生成都依赖于元素,可以指定出现重复键时的处理方案和保存结果的map。
@Testpublic 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(// keynew Function<Student, Integer>() {@Overridepublic Integer apply(Student student) {return student.getAge();}},// valuenew Function<Student, List<Student>>() {@Overridepublic List<Student> apply(Student student) {return Lists.newArrayList(student);}},// 有系统key的value怎么处理new BinaryOperator<List<Student>>() {@Overridepublic 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
这三个方法适用于汇总的,返回值分别是IntSummaryStatistics,LongSummaryStatistics,DoubleSummaryStatistics。
@Testpublic void summarizingInt() {List<Integer> list = Arrays.asList(1, 2, 3);IntSummaryStatistics ret = list.stream().collect(Collectors.summarizingInt(new ToIntFunction<Integer>(){@Overridepublic int applyAsInt(Integer value) {return value;}}));System.out.println(ret.getAverage());}
关于JDK8里面Stream部分内容我们就讲这么些,真心希望能帮助上大家。大家如果有什么疑问可以留言。最后,上面所有的实例代码在 https://github.com/tuacy/google-guava-study里面都可以找到。