9.2 Collectors 类分析
对应 Collectors 静态工厂类来说,其实共分为两种情况:
- 通过 CollectorImpl 内部类来实现
- 通过 reducing 方法来实现,reducing 方法本身又是通过 CollectorImpl 来实现的
其实上面自定义的会了之外,看 Collectors 类的实现,都会感到很亲切的。这里不说了。介绍几个上面没用过的方法:
9.2.1 mapping 多级缩减映射
Collector<T, ?, R>mapping(Function<? super T, ? extends U> mapper, Collector<? super U, A, R> downstream) : mapping 收集器在多级缩减中最有用,例如 groupingBy或partitioningBy的下游。例如,给定Person的流,以累积每个城市中的名字集合:
Map<City,Set<String>>lastNamesByCity =
people.stream().collect(groupingBy(Person::getLastName, mapping(Person::getLastName, toSet())));
9.2.2 collectingAndThen 结果集再映射
collectingAndThen(Collector<T,A,R> downstream, Function<R,RR> finisher) 调整 Collector 以执行其他完成转换。例如,可以调整 toList()收集器以始终生成一个不可变列表:
List<String> people = people.stream().collect(collectingAndThen(toList(), Collections::unmodifi
ableList));
public static<T,A,R,RR> Collector<T,A,RR> collectingAndThen(Collector<T,A,R> downstream,
Function<R,RR> finisher) {
Set<Collector.Characteristics> characteristics = downstream.characteristics();
if (characteristics.contains(Collector.Characteristics.IDENTITY_FINISH)) {// 这里移除了 IDENTITY_FINISH
if (characteristics.size() == 1)
characteristics = Collectors.CH_NOID;
else {
characteristics = EnumSet.copyOf(characteristics);
characteristics.remove(Collector.Characteristics.IDENTITY_FINISH);
characteristics = Collections.unmodifiableSet(characteristics);
}
}
return new CollectorImpl<>(downstream.supplier(),
downstream.accumulator(),
downstream.combiner(),
downstream.finisher().andThen(finisher),//这里在finisher后再添加了一个 Function,downstream.finisher() 原来就是一个 Function
characteristics);
}
移除了 identity, 在 finisher 后 用了 andThen 方法。做到结果集转换为其他类型。
9.2.3 summingInt 求和
为什么要用数组,而最后返回数组的唯一一个元素?
public static <T> Collector<T, ?, Integer>
summingInt(ToIntFunction<? super T> mapper) {
return new CollectorImpl<>(
() -> new int[1],//为什么要用数组呢
(a, t) -> { a[0] += mapper.applyAsInt(t); },
(a, b) -> { a[0] += b[0]; return a; },
a -> a[0], CH_NOID);
}
因为数组是个引用类型的,如果你弄的是数字,他就是固定值了,不能够传递。而且中间结果要求的是一个容器。
9.2.4 reducing 缩减数据源,对数据进行缩减操作,像求和、平均等都可以用该方法实现的,是一个收集器
reducing 实现:
使用identity初始值和op函数对输入的元素进行聚合操作
public static <T> Collector<T, ?, T>
reducing(T identity, BinaryOperator<T> op) {
return new CollectorImpl<>(
boxSupplier(identity),
(a, t) -> { a[0] = op.apply(a[0], t); },
(a, b) -> { a[0] = op.apply(a[0], b[0]); return a; },
a -> a[0],
CH_NOID);
}
private static <T> Supplier<T[]> boxSupplier(T identity) {
return () -> (T[]) new Object[] { identity };
}
reducing 是对数字的处理,用 boxSupplier 创建一个数组存放数字,通过,BinaryOperator 函数,接收两个同种类型的参数,进行操作。最终返回数组对象的第一个数据。
reducing 中间多了个Function,即流的数据先经过 Function 处理后的数据再跟初始值进行合并操作。
public static <T, U>
Collector<T, ?, U> reducing(U identity,
Function<? super T, ? extends U> mapper,
BinaryOperator<U> op) {
return new CollectorImpl<>(
boxSupplier(identity),
(a, t) -> { a[0] = op.apply(a[0], mapper.apply(t)); },
(a, b) -> { a[0] = op.apply(a[0], b[0]); return a; },
a -> a[0], CH_NOID);
}
未知流是否有数据的情况下,将流合并起来,返回 Optional 对象。
public static <T> Collector<T, ?, Optional<T>>
reducing(BinaryOperator<T> op) {
class OptionalBox implements Consumer<T> {
T value = null;
boolean present = false;
@Override
public void accept(T t) {
if (present) {
value = op.apply(value, t);
}
else {
value = t;
present = true;
}
}
}
return new CollectorImpl<T, OptionalBox, Optional<T>>(
OptionalBox::new, OptionalBox::accept,
(a, b) -> { if (b.present) a.accept(b.value); return a; },
a -> Optional.ofNullable(a.value), CH_NOID);
}
上面例子,累乘:
public static void main(String[] args)
{
IntStream stream = IntStream.of(2, 3, 4, 5, 6);
OptionalInt answer = stream.reduce((a, b) -> (a * b));
if (answer.isPresent()) {
System.out.println(answer.getAsInt());
}
else {
System.out.println("no value");
}
}
9.2.5 groupingBy 分组实现
groupingBy 的实现,是比较复杂的,我们,先从简单的开始。
public static <T, K> Collector<T, ?, Map<K, List<T>>>
groupingBy(Function<? super T, ? extends K> classifier) {
return groupingBy(classifier, toList());
}
T 流当中的元素的类型, ? 累加器类型,中间结果集类型, Map<K, List> 返回类型,K 分类器函数结果类型的key即Map的key。
public static <T, K, A, D>
Collector<T, ?, Map<K, D>> groupingBy(Function<? super T, ? extends K> classifier, Collector<? super T, A, D> downstream) {
return groupingBy(classifier, HashMap::new, downstream);
}
K 是分类器的结果类型,即Map中的Key,D 是返回值类型的,即Map中的value.
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) {
// Supplier A 下游收集器的中间累积类型
Supplier<A> downstreamSupplier = downstream.supplier();
// BiConsumer 第一个元素是中间结果类型,第二个元素是下一个元素类型
BiConsumer<A, ? super T> downstreamAccumulator = downstream.accumulator();
// 构造新的 accumulator 对像, Map<K, A> m 是新的中间累加器类型 T t 元素类型
BiConsumer<Map<K, A>, T> accumulator = (m, t) -> {
//classifier.apply(t) 返回一个K类型
K key = Objects.requireNonNull(classifier.apply(t), "element cannot be mapped to a null key");
// m 是 Map<K,A> 获取 key 对应的值
// computeIfAbsent 获取当前对象的value,如果为空,则采用接收寄来的Function,即 k -> downstreamSupplier.get(),如果该方法返回来不为空,则给 m 对象 添加上该数据,返回对应key的值
A container = m.computeIfAbsent(key, k -> downstreamSupplier.get());
// 下游收集器累加器方法
downstreamAccumulator.accept(container, t);
};
// 部分结果合并在一起。 mapMerger 两个map的合并
BinaryOperator<Map<K, A>> merger = Collectors.<K, A, Map<K, A>>mapMerger(downstream.combiner());
// 强制类型转换,M 本身是 extends Map<K,D> 怎么可以强转Map<K,A> 呢,因为中间结果类型都是 Map<K,A> 类型,而mapFactory.get 调用的就是获取中间结果类型,所以可以强制成功,再者mapFactory 是一个中间结果类型。下面我用了一条实例说明
@SuppressWarnings("unchecked")
Supplier<Map<K, A>> mangledFactory = (Supplier<Map<K, A>>) mapFactory;
if (downstream.characteristics().contains(Collector.Characteristics.IDENTITY_FINISH)) {
return new CollectorImpl<>(mangledFactory, accumulator, merger, CH_ID);
}
else {
@SuppressWarnings("unchecked")
Function<A, A> downstreamFinisher = (Function<A, A>) downstream.finisher();
Function<Map<K, A>, M> finisher = intermediate -> {
intermediate.replaceAll((k, v) -> downstreamFinisher.apply(v));
@SuppressWarnings("unchecked")
M castResult = (M) intermediate;
return castResult;
};
return new CollectorImpl<>(mangledFactory, accumulator, merger, finisher, CH_NOID);
}
}
Function<? super T, ? extends K> , 指输入类型是 T或者 T 之上的,输出类型是K或者K之下的.Map 的 key 是 K 类型。
Supplier mapFactory: map工厂,用来接收 Map对象实例。中间累加器的类型。
Collector<? super T, A, D> downstream 下游收集器类型,T 下游元素类型,A下游收集器中间累加器类型,D 返回的结果类型(Map 的 value 类型)
Map<City, Set<String>> namesByCity = people.stream()
.collect(groupingBy(Person::getCity,TreeMap::new,
mapping(Person::getLastName, toSet())));
根据城市分组,用TreeMap排序,再根据映射要求只需要将Person的名字收集成set集合。
java 泛型奇妙之处,其实只要我们能够确定两个类型是一直的,就可以强转,因为泛型底层是通过擦拭泛型转为Object 类型,所以 Object 之间转是没问题的,就是在用的时候是不是类型数据一直,所以这里给大家看的代码就是 用不同的泛型声明,给同样的数据类型绑定。
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
public class InterestingTest<K,T,V> {
private Map<K,T> Tmap = null;
private Map<K,V> Vmap = null;
private Function<T,V> Vf = null;
private Function<T,T> Tf = null;
public InterestingTest(Map<K,T> m,Function<T,V> f){
Tmap = m;
Vf = f;
// 这里就进行了强转 编译器只是警告
Tf = (Function<T, T>) f;
// 这个地方
Tmap.replaceAll((k,t)->Tf.apply(t));
Vmap = (Map<K, V>) Tmap;
}
public Map<K, V> getMap(){
return Vmap;
}
public static void main(String[] args) {
Map<String,Integer> map = new HashMap<>();
map.put("1", 1);
map.put("2", 2);
map.put("3", 3);
map.put("4", 4);
Map<String,String> newMap =
new InterestingTest<String,Integer,String>(map,Integer::toBinaryString)
.getMap();
newMap.entrySet().stream()
.forEach(e->{System.out.println(e.getKey()+" "+e.getValue());});
}
}
上面代码运行下来,编译器只有警告,没有编译错误。java实现不了真正的泛型,其实,对于java泛型的实现,只是编译器在编译阶段帮助你进行类型转型,像Map<String,Integer>类型,实际是保存你的对象的地址,对于Map来说,他只是存了Integer的地址,将Integer视为终极父类Object的子类,当你调用get(key)的时候,最后返回Object类型给你之前帮你强制类型转换成了Integer,结论,java 对于泛型的处理实在编译器上执行,且对强制类型(已知类型)的数据,强转就会报错。但对 泛型 强转 泛型 怎没有处理。
这是我从其他地方找到的,地址 https://my.oschina.net/joshuashaw/blog/487766建议大家都出看下,在这里,我说的也不是很清楚。
9.2.6 partitioningBy 分区
与 groupingBy 相比,分区比较简单了。
public static <T>
Collector<T, ?, Map<Boolean, List<T>>> partitioningBy(Predicate<? super T> predicate) {
return partitioningBy(predicate, toList());
}
public static <T, D, A>
Collector<T, ?, Map<Boolean, D>> partitioningBy(Predicate<? super T> predicate,
Collector<? super T, A, D> downstream) {
BiConsumer<A, ? super T> downstreamAccumulator = downstream.accumulator();
BiConsumer<Partition<A>, T> accumulator = (result, t) ->
downstreamAccumulator.accept(predicate.test(t) ? result.forTrue : result.forFalse, t);
BinaryOperator<A> op = downstream.combiner();
BinaryOperator<Partition<A>> merger = (left, right) ->
new Partition<>(op.apply(left.forTrue, right.forTrue),
op.apply(left.forFalse, right.forFalse));
Supplier<Partition<A>> supplier = () ->
new Partition<>(downstream.supplier().get(),
downstream.supplier().get());
if (downstream.characteristics().contains(Collector.Characteristics.IDENTITY_FINISH)) {
return new CollectorImpl<>(supplier, accumulator, merger, CH_ID);
}
else {
Function<Partition<A>, Map<Boolean, D>> finisher = par ->
new Partition<>(downstream.finisher().apply(par.forTrue),
downstream.finisher().apply(par.forFalse));
return new CollectorImpl<>(supplier, accumulator, merger, finisher, CH_NOID);
}
}
private static final class Partition<T>
extends AbstractMap<Boolean, T>
implements Map<Boolean, T> {
final T forTrue;
final T forFalse;
Partition(T forTrue, T forFalse) {
this.forTrue = forTrue;
this.forFalse = forFalse;
}
@Override
public Set<Map.Entry<Boolean, T>> entrySet() {
return new AbstractSet<Map.Entry<Boolean, T>>() {
@Override
public Iterator<Map.Entry<Boolean, T>> iterator() {
Map.Entry<Boolean, T> falseEntry = new SimpleImmutableEntry<>(false, forFalse);
Map.Entry<Boolean, T> trueEntry = new SimpleImmutableEntry<>(true, forTrue);
return Arrays.asList(falseEntry, trueEntry).iterator();
}
@Override
public int size() {
return 2;
}
};
}
}
为什么提供 Partition,因为用Map并不能确定分区只分两个。