【问题标题】:Collect Indices using Java8 Collectors使用 Java8 收集器收集索引
【发布时间】:2017-07-20 19:23:47
【问题描述】:

我有一个形状流

Stream<Shape> shapes = Arrays.asList(TRIANGLE, CIRCLE, SQUARE, SQUARE, CIRCLE, TRIANGLE, TRIANGLE).stream();

Shape 在哪里

public enum Shape {TRIANGLE, CIRCLE, SQUARE}

如何编写将形状流分组为 Map&lt;Shape, List&lt;Integer&gt;&gt; 这样按形状收集索引?

public Map<Shape, List<Integer>> indexedPartition(Stream<Shape> shapes) {
//code here
}

在函数 indexedPartition 的当前示例输出中看起来像

TRIANGE -> {0, 5, 6} 
CIRCLE -> {1, 4} 
SQUARE -> {2, 3}

在 Scala 中我会做类似的事情

val indices = Stream.from(0)

object Shape extends Enumeration {
  type Shape = Value
  val CIRCLE, TRIANGLE, SQUARE = Value
}

val shapes = Stream(Shape.TRIANGLE, Shape.CIRCLE, Shape.SQUARE, Shape.SQUARE, Shape.CIRCLE, Shape.TRIANGLE, Shape.TRIANGLE)

(shapes zip indices).groupBy{ case (s, i) => s }.mapValues(l => l.map(_._2))
//res0: scala.collection.immutable.Map[Shape.Value,List[Int]] = Map(SQUARE -> List(2, 3), TRIANGLE -> List(0, 5, 6), CIRCLE -> List(1, 4))

我尝试在 java 中使用 Collectors.groupingBy,但我无法理解它。

【问题讨论】:

    标签: scala java-8 collectors


    【解决方案1】:

    通过索引流式传输:

    import static java.util.stream.Collectors.groupingBy;
    
    
    List<Shape> shapes = Arrays.asList(TRIANGLE, CIRCLE, SQUARE, SQUARE, CIRCLE, TRIANGLE, TRIANGLE);
    
    result = IntStream.range(0, shapes.size())
            .boxed()
            .collect(groupingBy(shapes::get));
    

    【讨论】:

    • 你能编辑你对Stream&lt;Shape&gt; shapes的回答吗?
    • @ApoorvIngle 你不能为Stream 做这件事——因为它没有索引
    • @Eugene 如何使用Streams.zip 或类似的构造来创建一个元组,然后像我在我的 scala sn-p 中演示的那样收集它。还是我对 Java Collectors.collect 的看法完全错误?
    • @ApoorvIngle 我对 scala 的了解不足以在这里为您提供帮助,但是 java 中的流没有索引 - 您可以从 Set 创建一个流,那将是什么索引?
    【解决方案2】:

    可以实现Collector,它记录在流处理结束时遇到的元素的索引。当然,这个数字对于无序流没有意义,如果你有改变大小的中间操作,比如filterflatMap,这个数字将不再反映原始数组中的位置。

    class IndexCollector<K> {
        int total;
        Map<K,List<Integer>> map = new HashMap<>();
    
        public static <T> Collector<T,?,Map<T,List<Integer>>> get() {
            return Collector.of(IndexCollector<T>::new,
                (c,t) -> c.map.computeIfAbsent(t, x -> new ArrayList<>()).add(c.total++),
                (c1,c2) -> merge(c1, c2),
                c -> c.map);
        }
        static <T> IndexCollector<T> merge(IndexCollector<T> a, IndexCollector<T> b) {
            if(a.total == 0) return b;
            if(b.total != 0) {
                int offset = a.total;
                b.map.forEach((t,l) -> {
                    List<Integer> target = a.map.computeIfAbsent(t, x -> new ArrayList<>());
                    for(Integer i: l) target.add(i+offset);
                });
                a.total += b.total;
            }
            return a;
        }
    }
    

    可以像这样使用

    Map<Shape, List<Integer>> map =
        Stream.of(TRIANGLE, CIRCLE, SQUARE, SQUARE, CIRCLE, TRIANGLE, TRIANGLE)
              .collect(IndexCollector.get());
    
    map.forEach((shape,list) -> System.out.printf("%-9s%s%n", shape, list));
    

    【讨论】:

    • 我也想过这个问题......依赖于元素到达收集器按遭遇顺序这一事实,但由于你所说的原因,我放弃了这个想法。仍然 +1。
    • 感谢您的回答@Holger。你不觉得这实现起来很冗长/乏味,尤其是当我们在过滤/flatMap 时不能保证保留索引时。有没有一种方法可以像在 Scala 中一样提供一个外部索引生成器,该生成器与流中的每个形状元素绑定?
    猜你喜欢
    • 2023-03-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-03-04
    • 1970-01-01
    • 2016-12-25
    • 1970-01-01
    相关资源
    最近更新 更多