【问题标题】:Transform list to mapping using java streams使用 java 流将列表转换为映射
【发布时间】:2018-06-04 17:14:47
【问题描述】:

我的代码中重复了以下模式:

class X<T, V>
{
    V doTransform(T t) {
        return null; // dummy implementation
    }

    Map<T, V> transform(List<T> item) {
        return item.stream().map(x->new AbstractMap.SimpleEntry<>(x, doTransform(x))).collect(toMap(x->x.getKey(), x->x.getValue()));
    }
}

要求使用 AbstractMap.SimpleEntry 既麻烦又笨重。 Linqs 对匿名类型的使用更加优雅。

有没有更简单的方法来使用流来实现这一点?

提前致谢。

【问题讨论】:

    标签: java java-stream


    【解决方案1】:

    你可以在值映射器中调用doTransform

    Map<T, V> transform(List<T> item) {
        return item.stream().collect(toMap(x -> x, x -> doTransform(x)));
    }
    

    【讨论】:

      【解决方案2】:

      不幸的是,Java 没有与 C# 的匿名类型完全等价的。

      在这种特定情况下,您不需要 @Jorn Vernee 建议的中间 map 操作。相反,您可以在 toMap 收集器中执行键和值提取。

      但是,当您认为需要 C# 的匿名类型之类的东西时,您可能会考虑:

      1. anonymous objects(根据您的用例,可能并不总是您想要的)
      2. Arrays.asList(...)List.of(...)(根据您的用例,可能并不总是您想要的)
      3. 一个数组(根据您的用例,可能并不总是您想要的)

      最终,如果您真的需要映射到可以包含两种不同类型元素的东西,那么我会坚持使用AbstractMap.SimpleEntry

      也就是说,您当前的示例可以简化为:

      Map<T, V> transform(List<T> items) {
          return items.stream().collect(toMap(Function.identity(),this::doTransform));
      }
      

      【讨论】:

      • 很好!,我的回答可能有点懒惰;)还有自 Java 9 以来的 Map.entry() 工厂方法,它更短一些。
      【解决方案3】:

      在这个具体的例子中,根本不需要做中间存储:

      Map<T, V> transform(List<T> item) {
          return item.stream().collect(toMap(x -> x, x -> doTransform(x)));
      }
      

      但如果您需要,Java 9 提供了一种更简单的工厂方法,

      Map<T, V> transform(List<T> item) {
          return item.stream()
                     .map(x -> Map.entry(x, doTransform(x)))
                     .collect(toMap(x -> x.getKey(), x -> x.getValue()));
      }
      

      只要你不必与null打交道。

      这里可以使用匿名内部类,

      Map<T, V> transform(List<T> item) {
          return item.stream()
                     .map(x -> new Object(){ T t = x; V v = doTransform(x); })
                     .collect(toMap(x -> x.t, x -> x.v));
      }
      

      但效率较低。它是一个内部类,它捕获对周围this 的引用,它还捕获x,因此您有两个字段,t 和用于捕获x 的合成字段,用于同一件事。

      后者可以通过使用方法来规避,例如

      Map<T, V> transform(List<T> item) {
          return item.stream()
                     .map(x -> new Object(){ T getKey() { return x; } V v = doTransform(x); })
                     .collect(toMap(x -> x.getKey(), x -> x.v));
      }
      

      但这并没有增加可读性。

      唯一真正的匿名类型是为 lambda 表达式生成的类型,它们可用于通过高阶函数存储信息:

      Map<T, V> transform(List<T> item) {
          return item.stream()
                     .map(x -> capture(x, doTransform(x)))
                     .collect(HashMap::new, (m,f) -> f.accept(m::put), HashMap::putAll);
      }
      
      public static <A,B> Consumer<BiConsumer<A,B>> capture(A a, B b) {
          return f -> f.accept(a, b);
      }
      

      但是,如果您在更复杂的场景中尝试这样做,您很快就会遇到 Java 类型系统的限制(它仍然不是一种函数式编程语言)。

      【讨论】:

        猜你喜欢
        • 2021-02-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-01-20
        相关资源
        最近更新 更多