惯用地枚举 Java 中的对象流 8

Idiomatically enumerating a Stream of objects in Java 8

如何使用 Java 8 种流方法(例如,对于数组 T[] values,创建Map<T,Integer> 其中 Map.get(values[i]) == i 的计算结果为 true)?

目前,我正在定义一个匿名 class,它会增加一个 int 字段以用于 Collectors.toMap(..) 方法:

private static <T> Map<T, Integer> createIdMap(final Stream<T> values) {
    return values.collect(Collectors.toMap(Function.identity(), new Function<T, Integer>() {

        private int nextId = 0;

        @Override
        public Integer apply(final T t) {
            return nextId++;
        }

    }));
}

但是,使用 Java 8 流 API 是否没有更 concise/elegant 的方法? — 如果可以安全地并行化,则加分。

我会这样做:

private static <T> Map<T, Integer> createIdMap2(final Stream<T> values) {
    List<T> list = values.collect(Collectors.toList());
    return IntStream.range(0, list.size()).boxed()
            .collect(Collectors.toMap(list::get, Function.identity()));
}

为了并行,可以改成

   return IntStream.range(0, list.size()).parallel().boxed().
                (...)

如果存在重复元素,您的方法将失败。

除此之外,您的任务需要可变状态,因此可以用 Mutable reduction 解决。当我们填充地图时,我们可以简单地使用地图的大小来获取未使用的 id。

比较棘手的部分是合并操作。以下操作只是重复右图的分配,这将处理潜在的重复项。

private static <T> Map<T, Integer> createIdMap(Stream<T> values) {
    return values.collect(HashMap::new, (m,t) -> m.putIfAbsent(t,m.size()),
        (m1,m2) -> {
            if(m1.isEmpty()) m1.putAll(m2);
            else m2.keySet().forEach(t -> m1.putIfAbsent(t, m1.size()));
        });
}

如果我们依赖独特的元素,或者插入一个明确的distinct(),我们可以使用

private static <T> Map<T, Integer> createIdMap(Stream<T> values) {
    return values.distinct().collect(HashMap::new, (m,t) -> m.put(t,m.size()),
        (m1,m2) -> { int leftSize=m1.size();
            if(leftSize==0) m1.putAll(m2);
            else m2.forEach((t,id) -> m1.put(t, leftSize+id));
        });

}

对比Andremoniy提供的解决方案先将输入流转为List。我更愿意以不同的方式来做,因为我们不知道 "toList()" 和 "list.get(i)" 的成本,而且没有必要创建一个额外的列表,它可以小也可以大

private static <T> Map<T, Integer> createIdMap2(final Stream<T> values) {
    final MutableInt idx = MutableInt.of(0); // Or: final AtomicInteger idx = new AtomicInteger(0);        
    return values.collect(Collectors.toMap(Function.identity(), e -> idx.getAndIncrement()));
}

不管问题如何,我认为在方法中将流作为参数传递是一个糟糕的设计。