将IntStream收集到Map时出现错误

12
以下代码
String[] values = ...
.... 
Map<String, Object> map = new HashMap<>();
for (int i = 0; i < values.length; i++) {
    map.put("X" + i, values[i]);
}

被IntelliJ转换为:

Map<String, Object> map = IntStream.range(0, values.length)
        .collect(Collectors.toMap(
                i -> "X" + i,
                i -> values[i],
                (a, b) -> b));
可以缩写为。
Map<String, Object> map = IntStream.range(0, values.length)
            .collect(Collectors.toMap(
                    i -> "X" + i,
                    i -> values[i]));

这2个版本无法编译。

IntelliJ提示在values[i]中i的类型存在问题:

不兼容的类型。
需要:int
找到:java.lang.Object

编译器报错:

Error:(35, 17) java: method collect in interface java.util.stream.IntStream cannot be applied to given types;
required: java.util.function.Supplier,java.util.function.ObjIntConsumer,java.util.function.BiConsumer
found: java.util.stream.Collector>
reason: cannot infer type-variable(s) R
(actual and formal argument lists differ in length)

有人能解释为什么会出现这种情况吗?


values是什么,你能包含它的声明吗?另外,关于IntelliJ的建议,似乎不一致。只需在声明和循环之间放置一个打印map语句即可。它将不再建议您用collect替换 - Naman
1
String[] values = ... - msayag
1
我想象中Collector不支持原始类型,这可能是为什么Lambda表达式被转换为Object的原因,因为boxed()方法可以解决它。 - Jacob G.
你应该无论如何向他们报告一个错误。 - everton
问题单:https://youtrack.jetbrains.com/issue/IDEA-180417 - msayag
1个回答

10
System.out.print(map);

在声明和循环之间放置语句,就不会再建议您替换为collect


使用IntStream#collect时,编译失败的原因是collect方法的实现需要三个指定的参数。这也可以在错误信息中看到。

Collectors.toMap(i -> "X" + i, i -> values[i])

将会导致只有一个类型为Collector的参数。


更好的转换表达式的方法是:

  • 要么使用forEach

    Map<String, Object> map;
    IntStream.range(0, values.length).forEach(i -> map.put("X" + i, values[i]));
    
  • 或者使用boxed()IntStream转换为Stream<Integer>,如下所示:

  • Map<String, Object> map = IntStream.range(0, values.length).boxed()
               .collect(Collectors.toMap(i -> "X" + i, i -> values[i], (a, b) -> b));
    
  • 或者如@Holger所建议的,您可以避免使用forEach和装箱开销,并修改结构以利用IntStream.collect三个参数的变体,如下所示:

  • Map<String, Object> map = IntStream.range(0, values.length)
               .collect(HashMap::new, (m,i) -> m.put("X"+i,values[i]), Map::putAll);
    

我会使用第一个。第二个,带有boxed将导致在Integer和int之间进行过多的装箱和拆箱。 - Shirkam
虽然你的解决方案是有效的,但它们并没有回答我的问题:为什么它不能编译? - msayag
@msayag 已编辑。虽然考虑到日志是自说明的,但使用boxed将IntStream转换为Stream,而该类中的collect被重载以接受传递的Collector - Naman
4
使用三个参数的变量:Map<String, Object> map = IntStream.range(0, values.length) .collect(HashMap::new, (m,i) -> m.put("X"+i,values[i]), Map::putAll); 可以避免 forEach 代码中的装箱开销。 - Holger
@Holger 感谢您的见解。已更新。之前是根据IntelliJ的建议提出的 :) - Naman

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接