如何使用stream - Java 8将List转换为带索引的Map?

Let*_*far 16 java java-8 java-stream

我已经创建了计算字母表中每个字符的方法.我正在学习流(函数式编程)并尝试尽可能多地使用它们,但在这种情况下我不知道该怎么做:

private Map<Character, Integer> numerateAlphabet(List<Character> alphabet) {
    Map<Character, Integer> m = new HashMap<>();
    for (int i = 0; i < alphabet.size(); i++)
        m.put(alphabet.get(i), i);
    return m;
}
Run Code Online (Sandbox Code Playgroud)

那么,如何使用Java 8流重写它呢?

Mis*_*sha 36

避免有状态索引计数器,如AtomicInteger其他答案中提供的基于解决方案.如果流是并行的,它们将失败.相反,流式传输索引:

IntStream.range(0, alphabet.size())
         .boxed()
         .collect(toMap(alphabet::get, i -> i));
Run Code Online (Sandbox Code Playgroud)

上面假设传入列表不应该有重复的字符,因为它是一个字母表.如果您有可能重复元素,那么多个元素将映射到相同的键,然后您需要指定合并功能.例如,您可以使用(a,b) -> b(a,b) ->a作为toMap方法的第三个参数.

  • 同样,这假定了快速随机访问。通常这不是问题,但最好明确提及。 (2认同)
  • @Gevorg`AtomicInteger`的线程安全性将确保每个数字都会被精确发射一次。问题在于,在并行流下,它们将被分配给乱序的字母。 (2认同)

i_a*_*ero 12

最好用Function.identity()它代替i->i:

IntStream.range(0, alphabet.size())
                .boxed()
                .collect(toMap(alphabet::get, Function.identity()));
Run Code Online (Sandbox Code Playgroud)

  • 为什么?是什么让 `Function.identity()` 比 `i-&gt;i` 更好? (3认同)

小智 8

您可以将流收集到地图并使用地图大小作为索引。

alphabet.stream()
    .collect(HashMap::new, (map, ch) -> map.put(ch, map.size()), Map::putAll);
Run Code Online (Sandbox Code Playgroud)


ash*_*n33 6

AtomicInteger在Java 8中使用流:

private Map<Character, Integer> numerateAlphabet(List<Character> alphabet) {
    AtomicInteger index = new AtomicInteger();
    return alphabet.stream().collect(
            Collectors.toMap(s -> s, s -> index.getAndIncrement(), (oldV, newV)->newV));
}
Run Code Online (Sandbox Code Playgroud)