用流映射的Java 8列表

时间:2015-09-30 06:13:50

标签: java dictionary collections java-8 java-stream

我有一个List<Item>集合。 我需要将其转换为Map<Integer, Item> 地图的关键字必须是集合中项目的索引。 我无法弄清楚如何使用流来做到这一点。 类似的东西:

items.stream().collect(Collectors.toMap(...));

任何帮助?

由于这个问题被确定为可能重复,我需要补充一点,我的具体问题是 - 如何获取列表中项目的位置并将其作为键值

6 个答案:

答案 0 :(得分:50)

您可以使用Stream创建IntStream个索引,然后将其转换为Map

Map<Integer,Item> map = 
    IntStream.range(0,items.size())
             .boxed()
             .collect(Collectors.toMap (i -> i, i -> items.get(i)));

答案 1 :(得分:12)

仅仅为了完整性的另一个解决方案是使用自定义收集器:

public static <T> Collector<T, ?, Map<Integer, T>> toMap() {
    return Collector.of(HashMap::new, (map, t) -> map.put(map.size(), t), 
            (m1, m2) -> {
                int s = m1.size();
                m2.forEach((k, v) -> m1.put(k+s, v));
                return m1;
            });
}

用法:

Map<Integer, Item> map = items.stream().collect(toMap());

此解决方案是并行友好的,不依赖于源(您可以使用无随机访问列表或Files.lines()或其他)。

答案 2 :(得分:9)

不要觉得你必须在流中使用所有。我会这样做:

AtomicInteger index = new AtomicInteger();
items.stream().collect(Collectors.toMap(i -> index.getAndIncrement(), i -> i));

只要您不对流并行化,这将有效,并且避免了潜在的昂贵和/或有问题(在重复的情况下)get()indexOf()操作。

(您不能使用常规int变量代替AtomicInteger,因为从lambda表达式外部使用的变量必须是有效的。注意,当无争议时(如本例所示),{{ 1}}非常快,并且不会造成性能问题。但如果它让你担心,你可以使用非线程安全的计数器。)

答案 3 :(得分:7)

这是更新的答案,没有评论中提到的任何问题。

Map<Integer,Item> outputMap = IntStream.range(0,inputList.size()).boxed().collect(Collectors.toMap(Function.identity(), i->inputList.get(i)));

答案 4 :(得分:1)

使用第三方库(例如protonpack,但还有其他库),您可以使用其索引zip {/ 1}}:

StreamUtils.zipWithIndex(items.stream())
    .collect(Collectors.toMap(Indexed::getIndex, Indexed::getValue));

虽然getIndex会返回long,但您可能需要使用类似的内容投射它:

i -> Integer.valueOf((int) i.getIndex())

答案 5 :(得分:1)

Eran's answer通常是随机访问列表的最佳方法。

如果您的List不是随机访问权限,或者您拥有Stream而不是List,则可以使用forEachOrdered

Stream<Item> stream = ... ;
Map<Integer, Item> map = new HashMap<>();
AtomicInteger index = new AtomicInteger();
stream.forEachOrdered(item -> map.put(index.getAndIncrement(), item));

如果流是并行的,这是安全的,即使目标地图是线程不安全的并且作为副作用进行操作。 forEachOrdered保证按顺序一次一个地处理项目。因此,并行运行不会产生任何加速。 (如果在forEachOrdered之前管道中存在昂贵的操作,可能会有一些加速。)