如何在没有并行行李的情况下使用 Java reduce 简单地将集合缩减为对象?

How to simply reduce a collection to an object using Java reduce without the parallel baggage?

所以我尝试使用 reduce 获取 List<String> 并创建一个 Map<String,List<Integer>>,其中地图的键是原始 List 的成员,并且该值是出现该键的索引的 List。一般来说,这是一个非常简单的操作,大多数其他语言使用 reduce、inject、fold 等。例如在 Javascript 中你可以这样做:

let mappings = someStrings.reduce( function( mappings, val, index ) {
    if( !mappings[val] ) {
        mappings[ val ] = [];
    }
    mappings[ val ].push( index );
    return mappings;
}, {});

然而,我发现 Java 中的 reduce 相当复杂,因为 Java 认为应该编写函数式操作,以便它们可以 "transparently" 并行化,但这增加了像这样的简单情况的额外开销。在Java中不得不写点比较蛋疼的东西:

List<String> headers = ...
Map<String,List<Integer>> mappings = IntStream.range(0, headers.size())
    .map( i -> new Pair<String,Integer>( headers.get(i), i ) )
    .reduce( new HashMap<>(), ( mapping, pair ) -> {
        if( !mappings.contains( pair.getFirst() ) ) {
            mappings.put( pair.getFirst(), new ArrayList<String>() );
        }
        mappings.get( pair.getFirst() ).add( pair.getSecond() );
        return mappings;
    }, ( x, y ) -> x );

如果我被迫实际合并 x 和 y,那么在合并复杂对象时对于简单的单线程情况会变得相当复杂。这确实显示了 Java 做出的设计决策所带来的复杂性。

我的问题是我是否遗漏了一些关于 reduce 的信息?有没有一种方法可以将并行设计减少(双关语)为单线程设计,从而使我看不到的更简单?并行选项很好,但大多数时候我对简单的单线程 reduce 感到满意。

您的操作看起来很复杂,原因有二。首先,您正在执行映射到对类型的不必要的中间操作。其次,您在工作中使用了错误的工具。而不是 Reduction you need Mutable Reduction.

完整的操作可以写成

Map<String,List<Integer>> mappings = IntStream.range(0, headers.size()).boxed()
    .collect(Collectors.groupingBy(headers::get));

另见 the groupingBy collector