我得到了一些自定义对象的流,我想创建一个MapMap<Integer, MyObject>
,每个对象的索引作为键。
Stream<String> myStream = Arrays.asList("one","two","three").stream();
Integer i = 0;
Map<Integer, String> result3 = myStream.collect(Collectors.toMap(x -> i++, x -> x));
显然,这不能编译,因为:
从lambda表达式引用的局部变量必须是final或有效final
是否有一种简单的方法可以将流的元素Map到它们的索引,以便上面示例的预期输出类似于:
{1=one, 2=two, 3=three}
7条答案
按热度按时间nqwrtyyt1#
可以使用
IntStream
来解决这个问题:您创建一个从
0
到list.size() - 1
的IntStream
(IntStream.range()
不包括流中的最后一个值),并将每个索引Map到列表中的值。此解决方案的优点是,它还可以处理并行流,而这在使用AtomicInteger
时是不可能的。所以这个例子的结果是:
要从
1
开始第一个索引,只需在收集期间添加1
:这将导致:
osh3o9ms2#
您的
i
变量不是有效的final变量。您可以使用
AtomicInteger
作为Integer
Package 器:我认为它有点笨拙,因为它只解决了有效的最终变量的问题。由于它是一个特殊的线程安全版本,它可能会引入一些开销。在answer by Samuel Philipp中的纯
stream
解决方案可能更适合您的需要。xytpbqjk3#
不需要随机访问源数据的干净解决方案是
这也适用于并行流。
如果这是一个循环任务,那么将逻辑放入一个可重用的收集器是值得的,包括一些优化:
然后可以像这样使用
或
nukf8bse4#
Guava具有静态方法
Streams#mapWithIndex
ogq8wdun5#
试试这个:
假设
String[] array = { "V","I","N","A","Y" };
输出:
um6iljoc6#
我们可以使用
List.indexOf(Object o)
方法来获取列表中元素的索引,同时构造Map
:如果列表中有重复的元素,那么第一个出现的元素的索引将被添加到最终的Map中。另外,为了解决键冲突期间Map中的合并错误,我们需要确保
toMap(keyMapper, valueMapper, mergeFunction)
提供了合并函数输出:
km0tfn4u7#
虽然来得有点晚,但这个问题可以通过使用自定义收集器非常优雅地解决-这意味着流的源可以是Collection之外的任何东西:
应静态导入的收集器: