【问题标题】:Understanding downstream reduction implementation了解下游减少实施
【发布时间】:2016-05-25 08:33:06
【问题描述】:

我正在尝试了解 JDK 中下游缩减的实现。就是这样:

   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> downstreamSupplier = downstream.supplier();
        BiConsumer<A, ? super T> downstreamAccumulator = downstream.accumulator();
        BiConsumer<Map<K, A>, T> accumulator = (m, t) -> {
            K key = Objects.requireNonNull(classifier.apply(t), "element cannot be mapped to a null key");
            A container = m.computeIfAbsent(key, k -> downstreamSupplier.get());
            downstreamAccumulator.accept(container, t);
        };
        BinaryOperator<Map<K, A>> merger = Collectors.<K, A, Map<K, A>>mapMerger(downstream.combiner());
        @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();  //1, <------------- HERE
            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);
        }
    }

//1downstreamFinisher 的类型为Function&lt;A, D&gt;。从类型参数声明&lt;T, K, D, A, M extends Map&lt;K, D&gt;&gt;来看,类型D不依赖于A。那么为什么我们将它转​​换为Function&lt;A, A&gt;。我认为,D 类型甚至可能不是A 的子类。

我错过了什么?

【问题讨论】:

    标签: java java-8 collectors


    【解决方案1】:

    如果下游收集器没有标识终结器并且终结器函数返回与中间容器类型不同的类型,则此收集器实际上违反了Map 的泛型类型安全性。

    在收集操作期间,地图将保存A 类型的对象,即中间容器类型。然后,在操作结束时,groupingBy 的finisher 会遍历map,将finisher 函数应用到每个值上,并将其替换为最终结果。

    当然,如果没有未经检查的操作,这是无法实现的。有多种方法可以做到这一点,您发布的变体将地图供应商的类型从 Supplier&lt;M&gt; 更改为 Supplier&lt;Map&lt;K, A&gt;&gt;(第一个未经检查的操作),因此编译器接受地图将保存 @987654326 类型的值@ 而不是 D。这就是为什么必须将finisher 函数更改为Function&lt;A,A&gt;(第二个未经检查的操作),以便它可以用于映射的replaceAll 操作,这似乎需要A 类型的对象,尽管它实际上是D。最后,必须将结果映射转换为M(第三个未经检查的操作),以获取供应商实际提供的预期结果类型M 的对象。

    正确的类型安全替代方法是使用不同的映射并通过使用转换中间映射值的结果填充结果映射来执行完成操作。这不仅可能是一项昂贵的操作,而且还需要中间地图的第二个供应商,因为所提供的供应商只生产适合最终结果的地图。显然,开发人员认为这是对类型安全的可接受的破坏。

    请注意,当您尝试使用强制类型安全的Map 实现时,您会注意到不安全的操作:

    Stream.of("foo", "bar").collect(Collectors.groupingBy(String::length,
        () -> Collections.checkedMap(new HashMap<>(), Integer.class, Long.class),
        Collectors.counting()));
    

    将使用此实现生成 ClassCastException,因为它尝试将中间容器(数组)而不是 Long 放入 Map。

    【讨论】:

      猜你喜欢
      • 2017-09-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-06-15
      • 1970-01-01
      • 1970-01-01
      • 2017-03-22
      相关资源
      最近更新 更多