我从文本文件中读取了以下代码行:
try (BufferedReader br = new BufferedReader(new InputStreamReader(Uio.decodeFrom(url)))) {
return br.lines()
.parallel()
.map(s -> s.split("\s+")) // split by whitespace
.collect(
Collectors.groupingByConcurrent(
arr -> arr[0], // String 1
Collectors.groupingByConcurrent(
arr -> arr[arr.length-1], // String 2
Collectors.counting()
)
)
);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
文本文件包含类似的数据
String1 ... cols ... String2
string1data ... otherdata ... string2data
...
我正在尝试按String1
和String2
进行分组,并得到它们的计数。那么最终结果应该是Map<String, Map<String, Long>>
。但是,对于上面的代码,编译器表示collect()
返回一个ConcurrentMap <Object, ConcurrentMap<Object, Long>>
。
为什么键不是字符串?
我可以复制此错误消息,但在错误消息中将String
替换为Object
似乎是在转移注意力。真正的问题是Java的泛型是不变的。
如果对collect
的调用返回的是ConcurrentMap<String, ConcurrentMap<String, Long>>
,则这与Map<String, Map<String, Long>>
不匹配,即使ConcurrentMap
是Map
。内部Map
类型必须完全匹配,不包含通配符和边界。
如果在返回类型中引入一个上界通配符,则编译时不会出错。让它返回类型Map<String, ? extends Map<String, Long>>
,这样内部的ConcurrentMap<String, Long>
就会匹配。
返回类型Map<String, ConcurrentMap<String, Long>>
也将起作用。
在解决泛型不变问题之前,还不清楚为什么String
没有被捕获。只是猜测:编译器还没有捕获String
,因为它首先发现了不变泛型问题。一旦不变泛型问题得到解决,它就会编译而不会出错,这意味着String
确实得到了推断。