JAVA8学习9-Collectors 类分析

9.2 Collectors 类分析

​ 对应 Collectors 静态工厂类来说,其实共分为两种情况:

  1. 通过 CollectorImpl 内部类来实现
  2. 通过 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并不能确定分区只分两个。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值