我有一个List<Item>
集合。
我需要将它转换为Map<Integer, Item>
地图的键必须是项目在集合中的索引。
我无法通过流弄清楚如何做到这一点。
类似于:
items.stream().collect(Collectors.toMap(...));
需要帮忙吗?
由于这个问题被识别为可能重复的,所以我需要补充一下我的具体问题是-如何获取列表中项目的位置并将其作为键值放置。
我有一个List<Item>
集合。
我需要将它转换为Map<Integer, Item>
地图的键必须是项目在集合中的索引。
我无法通过流弄清楚如何做到这一点。
类似于:
items.stream().collect(Collectors.toMap(...));
需要帮忙吗?
由于这个问题被识别为可能重复的,所以我需要补充一下我的具体问题是-如何获取列表中项目的位置并将其作为键值放置。
IntStream
创建一个索引的 Stream
,然后将它们转换为 Map
:Map<Integer,Item> map =
IntStream.range(0,items.size())
.boxed()
.collect(Collectors.toMap (i -> i, i -> items.get(i)));
为了完整起见,还有一种解决方案是使用自定义收集器:
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()
等)。toList()
)都可以正常工作。 - Tagir Valeev不要感觉你必须在/与直播中做 所有事情。我只会这样做:
AtomicInteger index = new AtomicInteger();
items.stream().collect(Collectors.toMap(i -> index.getAndIncrement(), i -> i));
只要不对流进行并行处理,这个方法就可以工作,并且避免了可能昂贵和/或有问题(在重复情况下)的get()
和indexOf()
操作。(你不能使用一个常规的int
变量代替AtomicInteger
,因为在lambda表达式外部使用的变量必须是有效的最终变量。请注意,当没有竞争时(如本例),AtomicInteger
非常快,不会造成性能问题。但如果你担心,你可以使用非线程安全的计数器。)
List.get()
操作很耗费资源,但建议使用AtomicInteger
? - HolgerLinkedList
时,它才真正有用。另一方面,AtomicInteger
的复杂度为O(1)
并不重要,因为在您自己承认的不支持并行操作的情况下,线程安全性的隐藏成本是一个问题。如果您以“不必在流中完成所有操作”开始回答,为什么不提供一个没有流的替代方案,比如一个直接的循环?这将比呈现一个被鼓励避免的流用法更好。 - HolgerList
的实现方式。你似乎对LinkedList
有偏见,但实际上它并没有什么问题,而且List
很容易就可以是它,或者甚至是另一种更昂贵的实现方式。为什么要猜测呢?这种方式总是最快的。 - Pepijn SchmitzLinkedList
有偏见,因为它已经存在了十五年以上的时间,这足以证明它在实际生活中并不实用。理论上的优势只有一个操作,即在任意索引处插入,但由于它必须为此分配内存并更新半打节点引用,因此这种优势并没有真正体现出来。只有在非常大的列表中,才需要使用 LinkedList
来超越 ArrayList
,然而对于大型列表,LinkedList
的疯狂内存开销将抵消它的优势。LinkedList
只在忽略内存效应的 O(…)
比较中获胜。 - Holger这是更新的答案,没有评论中提到的任何问题。
Map<Integer,Item> outputMap = IntStream.range(0,inputList.size()).boxed().collect(Collectors.toMap(Function.identity(), i->inputList.get(i)));
Item
,这将失败。 - MishaO(n²)
。 - Holgerlist.indexOf(i)
很_慢_。我不建议使用这种方法。 - Boris the Spider压缩
,然后就完成了:StreamUtils.zipWithIndex(items.stream())
.collect(Collectors.toMap(Indexed::getIndex, Indexed::getValue));
getIndex
返回一个long
,因此您可能需要使用类似以下内容进行转换:i -> Integer.valueOf((int) i.getIndex())
EntryStream.of(items).toMap();
。 - Jean-François SavardEntryStream
吗? - njzk2List
,其复杂度取决于列表实现中 get(i)
的访问时间。 - njzk2Eran'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
之前有昂贵的操作,可能会有一些加速。)
forEachOrdered
,您不需要 AtomicInteger
,只需使用 stream.forEachOrdered(item -> map.put(map.size(), item))
。读取非易失性字段 HashMap.size
,该字段无论如何都会更新,与在 AtomicInteger
中使用 CAS 没有什么区别。 - Tagir ValeevforEachOrdered
,这一点还没有被提到。 - Stuart MarkstoList()
并编写一个特殊的适配器(基于AbstractMap<Integer, T>
),将List<T>
适配到Map<Integer, T>
。将它们存储到HashMap
中只是浪费时间和内存。 - Tagir Valeev
items
进行EntryStream.of(items).toMap()
操作。详细文档请参阅这里。 - Tagir ValeevArrayList
),通过IntStream.range(0,list1.size()).mapToObj(idx -> doSomethingWith(list1.get(idx), list2.get(idx)))
将它们压缩在一起并不是很困难,结果将是并行友好的。 - Tagir Valeev