java 如何使用流将元素Map到它们的索引?

pzfprimi  于 2023-01-01  发布在  Java
关注(0)|答案(7)|浏览(332)

我得到了一些自定义对象的流,我想创建一个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}
nqwrtyyt

nqwrtyyt1#

可以使用IntStream来解决这个问题:

List<String> list = Arrays.asList("one","two","three");
Map<Integer, String> map = IntStream.range(0, list.size()).boxed()
        .collect(Collectors.toMap(Function.identity(), list::get));

您创建一个从0list.size() - 1IntStreamIntStream.range()不包括流中的最后一个值),并将每个索引Map到列表中的值。此解决方案的优点是,它还可以处理并行流,而这在使用AtomicInteger时是不可能的。
所以这个例子的结果是:

{0=one, 1=two, 2=three}

要从1开始第一个索引,只需在收集期间添加1

List<String> list = Arrays.asList("one", "two", "three");
Map<Integer, String> map = IntStream.range(0, list.size()).boxed()
        .collect(Collectors.toMap(i -> i + 1, list::get));

这将导致:

{1=one, 2=two, 3=three}
osh3o9ms

osh3o9ms2#

您的i变量不是有效的final变量。
您可以使用AtomicInteger作为Integer Package 器:

Stream<String> myStream = Arrays.asList("one","two","three").stream();
AtomicInteger atomicInteger = new AtomicInteger(0);
Map<Integer, String> result3 = myStream.collect(Collectors.toMap(x -> atomicInteger.getAndIncrement(), Function.identity()));

我认为它有点笨拙,因为它只解决了有效的最终变量的问题。由于它是一个特殊的线程安全版本,它可能会引入一些开销。在answer by Samuel Philipp中的纯stream解决方案可能更适合您的需要。

xytpbqjk

xytpbqjk3#

不需要随机访问源数据的干净解决方案是

Map<Integer,String> result = Stream.of("one", "two", "three")
    .collect(HashMap::new, (m,s) -> m.put(m.size() + 1, s),
        (m1,m2) -> {
            int offset = m1.size();
            m2.forEach((i,s) -> m1.put(i + offset, s));
        });

这也适用于并行流。
如果这是一个循环任务,那么将逻辑放入一个可重用的收集器是值得的,包括一些优化:

public static <T> Collector<T,?,Map<Integer,T>> toIndexMap() {
    return Collector.of(
        HashMap::new,
        (m,s) -> m.put(m.size() + 1, s),
        (m1,m2) -> {
            if(m1.isEmpty()) return m2;
            if(!m2.isEmpty()) {
                int offset = m1.size();
                m2.forEach((i,s) -> m1.put(i + offset, s));
            }
            return m1;
        });
}

然后可以像这样使用

Map<Integer,String> result = Stream.of("one", "two", "three")
    .collect(MyCollectors.toIndexMap());

Map<Integer,Integer> result = IntStream.rangeClosed(1, 1000)
    .boxed().parallel()
    .collect(MyCollectors.toIndexMap());
nukf8bse

nukf8bse4#

Guava具有静态方法Streams#mapWithIndex

Stream<String> myStream = Stream.of("one","two","three");
Map<Long, String> result3 = Streams.mapWithIndex(myStream, (s, i) -> Maps.immutableEntry(i + 1, s))
    .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

// {1=one, 2=two, 3=three}
System.out.println(result3);
ogq8wdun

ogq8wdun5#

试试这个:
假设String[] array = { "V","I","N","A","Y" };

Arrays.stream(array) 
        .map(ele-> index.getAndIncrement() + " -> " + ele) 
        .forEach(System.out::println);

输出:

0 -> V
1 -> I
2 -> N
3 -> A
4 -> Y
um6iljoc

um6iljoc6#

我们可以使用List.indexOf(Object o)方法来获取列表中元素的索引,同时构造Map

List<String> list = Arrays.asList("one","two","three");
 Map<Integer, String> result = list.stream()
                                   .collect(Collectors.toMap(
                                    k -> list.indexOf(k) + 1, 
                                    Function.identity(),
                                    (v1, v2) -> v2));

 System.out.println(result);

如果列表中有重复的元素,那么第一个出现的元素的索引将被添加到最终的Map中。另外,为了解决键冲突期间Map中的合并错误,我们需要确保toMap(keyMapper, valueMapper, mergeFunction)提供了合并函数
输出:

{1=one, 2=two, 3=three}
km0tfn4u

km0tfn4u7#

虽然来得有点晚,但这个问题可以通过使用自定义收集器非常优雅地解决-这意味着流的源可以是Collection之外的任何东西:

List<String> names = List.of("one", "two", "three");

names.stream()
.collect(indexed())
.forEach((number, name) -> System.out.println("%d: %s".formatted(number, name)));

应静态导入的收集器:

public static <T, R> Collector<T, ?, Map<Integer, T>> indexed()
{
    return Collector.of(
            LinkedHashMap::new, 
            (map, element) -> map.put(map.size() +1, element), 
            (left, right) -> {left.putAll(right); return left;});
}

相关问题