Java 8,整数流,整数流对流的索引进行分组?

Dan*_*iel 9 java dictionary java-8 java-stream

我得到了一个Integers流,我想按每个元素的值对元素的索引进行分组.
例如,{1, 1, 1, 2, 3, 3, 4}将整数分组为索引列表映射:

1 -> 0, 1, 2
2 -> 3
3 -> 4, 5
4 -> 6
Run Code Online (Sandbox Code Playgroud)

我尝试过使用stream,但还有一个额外的类:

@Test
public void testGrouping() throws Exception {
    // actually it is being read from a disk file
    Stream<Integer> nums = Stream.of(1, 1, 1, 2, 3, 3, 4);  
    // list to map by index
    int[] ind = {0};  // capture array, effectively final
    class Pair {
        int left;
        int right;

        public Pair(int left, int right) {
            this.left = left;
            this.right = right;
        }
    }

    Map<Integer, List<Integer>> map = nums.map(e -> new Pair(ind[0]++, e))
            .collect(Collectors.groupingBy(e -> e.right))
            .entrySet().parallelStream()
            .collect(Collectors.toConcurrentMap(
                    Map.Entry::getKey,
                    e -> e.getValue().parallelStream().map(ee -> ee.left).collect(Collectors.toList())
            ));
}
Run Code Online (Sandbox Code Playgroud)

我必须读取Stream,因为从我的应用程序中的磁盘文件中读取了整数流.
我觉得我这样做的方式非常不理想.是否有更好或更优雅的方式来做到这一点?
谢谢你的帮助.

Kon*_*kov 5

  1. 您可以使用该IntStream#range(int startInclusive, int endExclusive)方法来获取每个元素的索引。
  2. 然后使用该IntStream.boxed()方法将 转换IntStreamStream带有盒装Integers的 a
  3. 通过将每个索引映射到数组中的相应元素i -> array[i]并将重复元素收集到列表中来进行分组。

例如:

int[] array = {1, 1, 1, 2, 3, 3, 4};
Map<Integer, List<Integer>> result = 
        IntStream.range(0, array.length)
                 .boxed()
                 .collect(Collectors.groupingBy(i -> array[i], Collectors.toList()));
Run Code Online (Sandbox Code Playgroud)

更新:如果您没有数组(因此没有元素计数),但有 a Stream<Integer>,则可以将初始元素收集Stream到 a 中List<Integer>。这样你就会知道的大小Stream,然后你可以这样做:

Stream<Integer> = .... // The input stream goes here
//Collecting the input stream to a list, so that we get it's size.
List<Integer> list = stream.collect(Collectors.toList());
//Grouping process
Map<Integer, List<Integer>> result = 
    IntStream.range(0, list.size())
             .boxed()
             .collect(Collectors.groupingBy(i -> list.get(i), Collectors.toList()));
Run Code Online (Sandbox Code Playgroud)


Hol*_*ger 5

用一个小辅助方法来收集:

\n\n
class MapAndIndex {\n    Map<Integer,List<Integer>> map=new HashMap<>();\n    int index;\n\n    void add(int value) {\n        map.computeIfAbsent(value, x->new ArrayList<>()).add(index++);\n    }\n    void merge(MapAndIndex other) {\n        other.map.forEach((value,list) -> {\n            List<Integer> l=map.computeIfAbsent(value, x->new ArrayList<>());\n            for(int i: list) l.add(i+index);\n        } );\n        index+=other.index;\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

整个操作变成:

\n\n
Map<Integer,List<Integer>> map = IntStream.of(1, 1, 1, 2, 3, 3, 4)\n    .parallel()\n    .collect(MapAndIndex::new, MapAndIndex::add, MapAndIndex::merge).map;\n
Run Code Online (Sandbox Code Playgroud)\n\n

当您需要跟踪事先未知的索引时,您需要可变状态,因此需要名为\xe2\x80\x9cmutable归约\xe2\x80\x9d 的操作。

\n\n

请注意,您不需要\xe2\x80\x99tConcurrentMap这里。该Stream实现已经处理了并发性。它将MapAndIndex为每个涉及的线程创建一个容器,并merge在两个关联的线程完成其工作后调用两个容器上的操作。如果有顺序,这也将以保留顺序的方式完成,Stream如本例所示(否则记录索引的任务没有意义\xe2\x80\xa6)。

\n