【问题标题】:Map a Stream<T> to a Map<T, Long> that is counting?将 Stream<T> 映射到正在计数的 Map<T, Long>?
【发布时间】:2014-04-08 18:09:08
【问题描述】:

我有一个Stream&lt;T&gt;,是否可以生成一个Map&lt;T, Long&gt;,为每个元素增加一个计数?我还想知道它是否可以将T, Long 反转为Long, T,然后将其存储在Map 中,例如输入/输出:

Stream&lt;String&gt;的示例内容:

kgj
def
dgh

第一个问题想要的输出:

(kgj, 1)
(def, 2)
(dgh, 3)

最终想要的输出:

(1, kgj)
(2, def)
(3, dgh)

到目前为止我所拥有的,Stream&lt;VertexData&gt;

    primitives.stream()
            .flatMap(primitive -> primitive.getVertexData().stream())
            .distinct()
            .collect(Collectors.groupingBy(i -> i, Collectors.counting()))
            .forEach((k, v) -> System.out.println("k = " + k + " / v = " + v));

目前这会将Stream&lt;Primitive&gt; 转换为具有不同元素的Stream&lt;VertexData&gt;,然后转换为计算出现次数Map&lt;VertexData, Long&gt;,这不是我想要的,因为我希望它继续计数通过的每个元素

有没有办法按我的要求做?

【问题讨论】:

  • @assylias 我想说它提供了一种解决方法,并感谢您指导我解决这个问题,但这不是一个确定的答案,因此我认为您为什么将其发布为评论。
  • 在使用重复输入(例如 ["abc", "def", "abc"])时,您希望第一个问题的输出是什么?
  • @jpvee 它应该只计算不同的元素,(我已经在 我所拥有的中已经有了),使用流很容易通过 @ 获得稳定的不同元素987654336@.
  • 在 scala 中,我只想 x.zipWithIndex 和第二个:x.zipWithIndex.map(x =&gt; (x._2,x._1))

标签: java mapping java-8 java-stream


【解决方案1】:

您可以做的是编写自己的Collector,它会为您计算遇到的元素。类似以下的工作:

  Stream<String> strings = Stream.of("kgj", "def", "dgh");

  strings.distinct().collect(Collector.of(
          HashMap::new,
          (BiConsumer<Map<String, Long>, String>) (map, str) -> {
            map.put(str, Long.valueOf(map.size() + 1));
          },
          (left, right) -> {
            long s = left.size();
            right.entrySet().forEach(e -> left.put(e.getKey(),
                                                   Long.valueOf(
                                                           e.getValue()
                                                                   .longValue()
                                                           + s)));
            return left;
          })).forEach((k, v) -> System.out.println("k = " + k + " / v = " + v));

请注意(有些复杂的)组合器(Collector.of() 的第三个参数在并行处理流的情况下是必需的)。

【讨论】:

  • 知道这会对性能产生什么影响吗?有没有办法“禁用”并行处理,因为你永远不能并行计算......或者你可以吗?
【解决方案2】:

受@jpvee 的启发,我做了一个Collector,它回答了问题的最后一个子问题,并打算更清楚一些:

public static <T> Collector<T, ?, Map<Long, T>> indexing() {
    return Collector.of(
            HashMap::new,
            (map, t) -> map.put(Long.valueOf(map.size() + 1), t),
            (left, right) -> {
                final long size = left.size();
                right.forEach((k, v) -> left.put(k + size, v));
                return left;
            },
            Collector.Characteristics.CONCURRENT
    );
}

这是做什么的:

  • Stream&lt;T&gt; 上操作,它返回一个Map&lt;Long, T&gt;,该T 以流的遇到顺序为索引。
  • 它使用Collector.of,在正常情况下接受以下内容:
    • Map供应商
    • 一个累加器,将一个元素添加到Map
    • 一个组合器,它结合了可能来自并发访问的两个Maps。
    • 特性列表

所以我提供的是:

  • Supplier&lt;R&gt;:一个新的HashMap
  • BiConsumer&lt;R, T&gt;:获取地图并添加新元素的函数。
  • BinaryOperator&lt;R&gt;:结合两张地图的函数。
  • Collector.Charasteristics:该方法的特性,允许并发执行。

【讨论】:

  • 收集器的一种有趣的用法,但它最终的时间是 O(n log n)。原因是每次合并都复制了一半的元素,并且合并树中有 log n 个级别。这可以并行运行,但足够大的 n 将淹没任何并行加速。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-05-26
  • 1970-01-01
  • 2019-11-08
  • 2016-07-06
  • 2023-03-26
  • 2017-10-08
  • 1970-01-01
相关资源
最近更新 更多