代码之家  ›  专栏  ›  技术社区  ›  Andrew Cheong

是什么阻止我将map<string,list<string>>返回为map<string,collection<string>>?

  •  1
  • Andrew Cheong  · 技术社区  · 7 年前

    以下代码无法编译:

      public static Map<String, Collection<String>> convert(Collection<Foo> foos) {
        return foos == null ? Maps.newHashMap()
                            : foos.stream().collect(
                                Collectors.groupingBy(
                                    f -> f.getSomeEnum().name(),
                                    Collectors.mapping(Foo::getVal, Collectors.toList())));
      }
    

    除非我把返回类型改成 List :

      public static Map<String, List<String>> convert(Collection<Foo> foos) {
    

    通常我认为我应该能够,但也许泛型引入了一些歧义?

    准确的错误:

    Incompatible types. Required Map<String, Collection<String>> but 'collect' was inferred to R: no instance(s) of type variable(s) A, A, A, A, A, A, D, K, R, R, T, T, T, U exist so that List<T> conforms to Collection<String>
    

    我不认为细节是相关的,但只是以防万一:

    Foo 是这样的:

    public class Foo {
      private MyEnumType someEnum;
      private String val;
      public MyEnumType getSomeEnum();
      public String getVal();
    }
    

    我正试图转换 一张地图 S val 被分组 someEnum .

    2 回复  |  直到 7 年前
        1
  •  3
  •   xingbin    7 年前

    Map<String, List<String>> 不是的子类型 Map<String, Collection<String>> this 更多。

    您可以将返回类型声明为 Map<String, ? extends Collection<String>>

        2
  •  2
  •   Holger    7 年前

    方法签名 groupingBy mapping 对于下游收集器的结果类型没有差异。因此,您最终得到结果类型 List<T> toList() 收藏家。相反, toCollection 在其类型参数中有差异 C extends Collection<T> ,但即使没有,也不会有任何问题 ArrayList::new 到A Supplier<Collection<…>> :

    public static Map<String, Collection<String>> convert(Collection<Foo> foos) {
        return foos == null? new HashMap<>(): foos.stream().collect(
            Collectors.groupingBy(f -> f.getSomeEnum().name(),
                Collectors.mapping(Foo::getVal, Collectors.toCollection(ArrayList::new))));
    }
    

    这和 托尔斯特() ,考虑到当前的实现。但它不会受益于未来对 托尔斯特() 收藏家。另一种选择是继续使用 托尔斯特() ,但链A类型转换:

    public static Map<String, Collection<String>> convert(Collection<Foo> foos) {
        return foos == null? new HashMap<>(): foos.stream().collect(
            Collectors.groupingBy(f -> f.getSomeEnum().name(),
                Collectors.collectingAndThen(
                    Collectors.mapping(Foo::getVal, Collectors.toList()),
                    c -> c)));
    }
    

    由于这是一个扩展转换,转换函数非常简单 c -> c . 不幸的是,底层实现并不知道这个函数的琐碎之处,它将遍历结果映射的值,用应用这个函数的结果替换每个值。

    这可以通过一个特殊的加宽收集器来解决:

    public static <T,A,R extends W,W> Collector<T,A,W> wideningResult(Collector<T,A,R> original) {
        return Collector.of(original.supplier(), original.accumulator(), original.combiner(),
            original.finisher().andThen(t -> t),
            original.characteristics().toArray(new Collector.Characteristics[0]));
    }
    

    这基本上和 Collectors.collectingAndThen(original, t -> t) ,链接平凡的加宽转换函数。但它保留了原始收集器的特性,因此如果原始收集器具有 IDENTITY_FINISH 特性,我们仍然会有它,它允许跳过完成操作,在这种情况下 按比例分组 意味着它不需要遍历映射来应用函数。

    将其应用于实际用例会产生

    public static Map<String, Collection<String>> convert(Collection<Foo> foos) {
        return foos == null? new HashMap<>(): foos.stream().collect(
            Collectors.groupingBy(f -> f.getSomeEnum().name(),
                wideningResult(Collectors.mapping(Foo::getVal, Collectors.toList()))));
    }