jdk1.8的stream学习之四
今天来学习最后一个操作方法collect
日常流式操作后,需要对结果处理,则可以用这个方法来实现。
collect(Collector<? super P_OUT, A, R> collector)
这里我们首先要学一下Collectors这个类,这个类里有个静态内部类是Collector的实现类,也就是说,Collectors类里的静态方法内部实现都是基于Collector的实现类
Collectors.toCollection(Supplier<C> collectionFactory)
这个方法将流收集为一个实现了collection接口的容器类.如下所示,将一个流转化为一个hashSet.只要是collection接口的实现类,都可以在这里轻松转换。
HashSet<Integer> collect =Stream.of(2,3,6,8).collect(Collectors.toCollection(HashSet::new));
toList()
这个不多说,就是直接将流转化为一个Arraylist对象
toSet()
这个也不多说,转化为一个hashSet对象
joining()
这个不多说,将流中的元素拼接成字符串。流中的元素首先必须先转换为string类型。字符串的拼接是通过StringBuilder::append完成的。
String collect = Stream.of("hello", " ", "world").collect(Collectors.joining());
输出:hello world
joining(分隔符)
这个可以指定分割符号来进行字符串拼接。下面的代码结果同上
String collect = Stream.of("hello", "world").collect(Collectors.joining(" "));
输出:hello world
joining(分隔符,前缀,后缀)
这里方法里面可以指定分隔符,然后可以指定字符串的前缀,后缀
String collect = Stream.of("hello", "world").collect(Collectors.joining(",","{","}"));
输出:{hello,world}
mapping(Function<? super T, ? extends U> mapper,Collector<? super U, A, R> downstream) 函数
这个函数从源码的功能来看,mapping函数接收2个参数,第一个参数就是一个运算式,第二个参数是接受第一个参数运算后的结果,可以继续用Collector去处理。明白了这个数据的流向,那么我们来看看怎么用
比如:有一个字符串数组,想把字符串数组先转成大写,然后在拼接。可以有2种实现方式。下面的上下两种方式效果是一样的。
String collect = Stream.of("a","b","c").map(s->s.toUpperCase()).collect(Collectors.joining());
String collect1 = Stream.of("a","b","c").collect(Collectors.mapping(s -> s.toUpperCase(), Collectors.joining()));
System.out.println(collect);
System.out.println(collect1);
collectingAndThen(Collector<T,A,R> downstream,Function<R,RR> finisher)
见名知意,这个操作先把结果可以用自身函数处理一遍,然后可以继续用第二个函数把之前一补处理完的函数继续用新函数再次进行运算
比如如下图,将一个流处理完成后,想变成一个不可再次一被操作的集合,可以通过下列方式去完成。就是第一个参数处理的结果,作为第二个行为参数的入参,且一定是一个接受一个入参,然后进过运算有返回值的一个行为函数,最终结果是以第二个行为函数操作的结果。
Collection<String> collect = Stream.of("a", "b", "c").collect(Collectors.collectingAndThen(Collectors.toList(), a -> Collections.unmodifiableCollection(a)));
System.out.println(collect);
输出结果是一个不支持修改的集合。
counting 函数,是一个统计函数,返回流中元素的个数。
Long collect = Stream.of("a", "b", "c").collect(Collectors.counting());
System.out.println(collect);
输出结果是3,代表流中元素个数是3个
minBy maxBy 分别返回流中按照传入的比较规则返回最大最小值。
Optional<Person> collect = Stream.of(new Person("a", 11), new Person("b", 12), new Person("c", 22)).collect(Collectors.minBy((a, b) -> a.getAge() - b.getAge()));
Optional<Person> collect1 = Stream.of(new Person("a", 11), new Person("b", 12), new Person("c", 22)).collect(Collectors.minBy((a, b) -> b.getAge() - a.getAge()));
System.out.println(JSONObject.toJSONString(collect.get()));
System.out.println(JSONObject.toJSONString(collect1.get()));
操作返回结果是:
{"age":11,"name":"a"}
{"age":22,"name":"c"}
summingInt() summingLong() summingDouble()
需要传入一个基本类型的数字,然后这个函数会对传入的数字进行求和。
如下所示,有3个Person对象,想知道这三个对象的年龄和是多少,则可以通过这个函数来进行运算。运算过程如下,流中的每一个元素会传入到函数中,然后传入的对象需要转换成对于的基本类型,函数会将基本类型进行求和。
Integer collect = Stream.of(new Person("a", 11), new Person("b", 12), new Person("c", 22)).
collect(Collectors.summingInt(value -> value.getAge()));
System.out.println(collect);
操作返回结果是:
45
averagingInt() averagingLong() averagingDouble()
需要传入对应的基本类型,然后函数内部会对传入的数字进行求平均. 原理同上,需要将传入的对象转换为对应的基本类型,最后进行求平均
reducing()
这个函数和reduce函数的用法是一样的。这里再次用3个demo来说明下
一个参数:
Optional<Person> collect = Stream.of(new Person("张三", 13), new Person("李四", 13)).
collect(Collectors.reducing(BinaryOperator.minBy(Comparator.comparing(Person::getAge))));
System.out.println(collect.get());
2个参数:
Integer collect = Stream.of(1,2,3,4,5).
collect(Collectors.reducing(0,(a,b) -> a+b));
System.out.println(collect);
3个参数:
ArrayList<Integer> integers = Lists.newArrayList(1, 2, 3, 4, 5);
Integer collect = integers.parallelStream().
collect(Collectors.reducing(0, e -> (Integer) e, (c, d) -> c + d));
System.out.println(collect);
groupingBy 分组函数
分组函数的目标很简单,就是基于一定的规则进行分组。如下所示,对流中的数据进行奇数和偶数分组。
ArrayList<Integer> integers = Lists.newArrayList(1, 2, 3, 4, 5,6);
Map<Integer, List<Integer>> collect = integers.stream().
collect(Collectors.groupingBy(a -> (a)%2));
System.out.println(collect);
输出结果如下:{0=[2, 4, 6], 1=[1, 3, 5]}
groupingByConcurrent 很简单,就是分组后返回一个concurrentMap
这个用法和普通的groupingby是一样的用法,传入一个分组的标准。比如按人的年龄分组。如下:
ArrayList<Person> persons = Lists.newArrayList(new Person("a",11),new Person("b",12),new Person("c",11),new Person("b",14));
ConcurrentMap<Integer, List<Person>> collect = persons.stream().
collect(Collectors.groupingByConcurrent(a -> a.getAge()));
System.out.println(JSONObject.toJSONString(collect));
输出如下:
{11:[{"age":11,"name":"a"},{"age":11,"name":"c"}],12:[{"age":12,"name":"b"}],14:[{"age":14,"name":"b"}]}
partitioningBy 和groupingby返回的不一样的地方在于,返回的是一个key为布尔类型的key,也就是只能返回true,false两个key。
比如有几个人,判断是否是成年人。
ArrayList<Person> persons = Lists.newArrayList(new Person("a",11),new Person("b",12),new Person("c",18),new Person("b",19));
Map<Boolean, List<Person>> collect = persons.stream().
collect(Collectors.partitioningBy(a -> a.getAge() > 17));
System.out.println(JSONObject.toJSONString(collect));
输出结果如下:
{false:[{"age":11,"name":"a"},{"age":12,"name":"b"}],true:[{"age":18,"name":"c"},{"age":19,"name":"b"}]}
toMap 这个可以将流转换成map.
比如同样还是上面4个人,想用年龄做key,名字做value。成功的将一个对象的list转换成某2个属性的map集合
ArrayList<Person> persons = Lists.newArrayList(new Person("a",11),new Person("b",12),new Person("c",18),new Person("b",19));
Map<Integer, String> collect = persons.stream().
collect(Collectors.toMap(a -> a.getAge(), b -> b.getName()));
System.out.println(JSONObject.toJSONString(collect));
输出如下:
{18:"c",19:"b",11:"a",12:"b"}
toConcurrentMap 这个和上面一样,只是返回的是ConcurrentMap
summarizingInt 统计类型类操作。可以将流中的元素转换成int类型。返回一个IntSummaryStatistics对象。这个对象可以做统计,求平均数,最大,最小,和等操作。
比如想求上面列表中人的年龄之和。平均数,最大年龄,最小年龄
ArrayList<Person> persons = Lists.newArrayList(new Person("a",11),new Person("b",12),new Person("c",18),new Person("b",19));
IntSummaryStatistics collect = persons.stream().
collect(Collectors.summarizingInt(a -> a.getAge()));
System.out.println(collect.getAverage());
System.out.println(collect.getCount());
System.out.println(collect.getMax());
System.out.println(collect.getMin());
System.out.println(collect.getSum());
这里流操作基本就学完了。