在上一篇文章中,我们介绍了java8 中 stream API 的简单使用,其中包括对于stream流操作的相关方法,我们可以借助它对于集合进行相关的处理,下面要介绍收集器,就是对于上一篇中的处理之后的数据如何安放做出动作,相比较于指令式的代码,函数式的代码目的就更加清晰,表现就更加形象,而且代码的复用能力更强,结合上一篇中的例子,我们先抛出一个简单的收集器的例子,然后我们再进一步分析。
-
例子:(1)将数组中的元素加倍并去除中间重复数据。
一种解法,使用Collectors静态工厂类进行处理,Set
integerDouble = integers.stream() .map(x -> x * 2) .collect(Collectors.toSet());
可以看到我们使用到了 collect(Collectors.toSet());
查看源码 :
很明显,HashSet::new, Set::add 等函数引用是对于添加元素到Set集合中的操作。
public static
Collector> toSet() {
return new CollectorImpl<>((Supplier>) HashSet::new, Set::add,
(left, right) -> { left.addAll(right); return left; },
CH_UNORDERED_ID);
}
再进一步,返回了一个CollectImpl对象,带入参数都作为其构造函数的参数。
CollectorImpl(Supplier supplier,
BiConsumer accumulator,
BinaryOperator combiner,
Set characteristics) {
this(supplier, accumulator, combiner, castingIdentity(), characteristics);
}
可以看到suppiler -> 数据源提供者
BiConsumer -> 数据消费者 -> 用来提供数据接收和数据
BinaryOperator -> 数据处理器 -> 集合数据
Set
那么在对于每一次数据流的到来,收集器就按照上面CollectImpl返回对象对数据进行归约操作,最终得到一个Set 集合。
-
有哪些常用的收集器?
(1) Collectors 自带简单型
Collectors.counting(), 更多请参见Collectors源码integers.stream() .collect(Collectors.counting()).intValue();
(2)自带传参型
如下面这个:如何使用它,选出最大值,数据源是
public static Collector>
maxBy(Comparator super T> comparator) {
return reducing(BinaryOperator.maxBy(comparator));
}
实现Comparator :
StringList.stream().collect(Collectors.maxBy(
(Comparator) (o1, o2) -> {
int o1_lengh = o1.split(" ").length;
int o2_lengh = o2.split(" ").length;
if (o1_lengh > o2_lengh) {
return 1;
} else if (o1_lengh == o2_lengh){
return 0;
}else {
return -1;
}
}
)).ifPresent(s -> System.out.println(s));
(3) 分组
我们经常在sql 里面会使用到group by, 那么java8 中有没有提供类似的操作? 使得我们不用命令式编程就可以完成分组操作,答案是有,下面就是一个简单的例子:按照所在城市进行分组,当然也可以进行多分组,需要对上一次分组进行嵌套。
Map> listMap = personList.stream()
.collect(Collectors
.groupingBy(p -> p.getCity()
)
);
listMap.forEach((x,y) -> {
System.out.println(x);
y.stream().forEach(z -> System.out.println(z.getName()));
});
(2)分区
按业务条件进行分区,一般就是true or false.
Map> personLists =
personList.stream().collect(
Collectors.partitioningBy(x -> "Shanghai".equalsIgnoreCase(x.getCity())));
- 如何自己实现收集器
首先我们需要研究下Collectors.
查看源码,可以发现实际上起到作用的是其内部类 CollectorImpl, 这个类实现了
Collector 接口,我们可以照样画葫芦,把我们的业务逻辑实现在该实现类中,实现业务需要的收集器实现类。
public static >
Collector toCollection(Supplier collectionFactory) {
return new CollectorImpl<>(collectionFactory, Collection::add,
(r1, r2) -> { r1.addAll(r2); return r1; },
CH_ID);
}
public static
Collector> toList() {
return new CollectorImpl<>((Supplier>) ArrayList::new, List::add,
(left, right) -> { left.addAll(right); return left; },
CH_ID);
}
具体:
static class CollectorImpl implements Collector {
private final Supplier supplier;
private final BiConsumer accumulator;
private final BinaryOperator combiner;
private final Function finisher;
private final Set characteristics;
CollectorImpl(Supplier supplier,
BiConsumer accumulator,
BinaryOperator combiner,
Function finisher,
Set characteristics) {
this.supplier = supplier;
this.accumulator = accumulator;
this.combiner = combiner;
this.finisher = finisher;
this.characteristics = characteristics;
}
CollectorImpl(Supplier supplier,
BiConsumer accumulator,
BinaryOperator combiner,
Set characteristics) {
this(supplier, accumulator, combiner, castingIdentity(), characteristics);
}
@Override
public BiConsumer accumulator() {
return accumulator;
}
@Override
public Supplier supplier() {
return supplier;
}
@Override
public BinaryOperator combiner() {
return combiner;
}
@Override
public Function finisher() {
return finisher;
}
@Override
public Set characteristics() {
return characteristics;
}
}
因此如果我们要实现一个集合器的话,那我们就要实现对应的各个部分:
Supplier supplier,
BiConsumer accumulator,
BinaryOperator combiner,
Function finisher,
Set characteristics
首先让我们给自己提出一个需求: 收集年龄大于超过23的人,因此这边我们可以定义一下收集器。
public interface Collector
其中T,A,R 分别表示
* @param the type of input elements to the reduction operation
* @param the mutable accumulation type of the reduction operation
(often hidden as an implementation detail)
* @param the result type of the reduction operation
我们传入;
(1)suppiler : 数据源的提供者。
@Override
public Supplier> supplier() {
return () -> {
return new ArrayList();
};
}
(2)accumulator : 数据消费则
@Override
public BiConsumer, Integer> accumulator() {
return (List list, Integer ele) -> {
if(isBiggerThan(ele).test(ele)){
list.add(ele);
}
};
}
(3)combiner : 数据操作器
@Override
public BinaryOperator> combiner() {
return (List x ,List y) -> {
x.addAll(y);
return x;
};
}
(4)Finisher: 结束者
@Override
public Function, List> finisher() {
return Function.identity();
}
(5)characteristics : 集合器特性
@Override
public Set characteristics() {
return Collections.unmodifiableSet(EnumSet.of(IDENTITY_FINISH));
}
那接下来调用:
List ageList = Lists.newArrayList(23,4,5,2,67,42);
ageList.stream().collect(new AgeCollectors()).forEach(System.out::println);
到这边为止,我们就构造了一个简单的自定义的收集器。