Java 如何使用流将元素映射到它们的索引?

Java 如何使用流将元素映射到它们的索引?,java,java-8,java-stream,Java,Java 8,Java Stream,我得到了一些自定义对象的流,我想创建一个mapmap,其中每个对象的索引都是键。给你一个简单的例子: Stream<String> myStream = Arrays.asList("one","two","three").stream(); Integer i = 0; Map<Integer, String> result3 = myStream.collect(Collectors.toMap(x -> i++, x -> x)); 试试这个: 假设S

我得到了一些自定义对象的流,我想创建一个map
map
,其中每个对象的索引都是键。给你一个简单的例子:

Stream<String> myStream = Arrays.asList("one","two","three").stream();
Integer i = 0;
Map<Integer, String> result3 = myStream.collect(Collectors.toMap(x -> i++, x -> x));
试试这个:

假设
String[]数组={“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

i
变量不是有效的最终变量

您可以使用as
Integer
包装器:

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()));
Stream myStream=Arrays.asList(“一”、“二”、“三”).Stream();
AtomicInteger AtomicInteger=新的AtomicInteger(0);
Map result3=myStream.collect(Collectors.toMap(x->atomicInteger.getAndIncrement(),Function.identity());
我认为它有点麻烦,因为它只解决了有效的最终变量的问题。因为它是一个特殊的线程安全版本,所以可能会引入一些开销。中的纯
解决方案可能更适合您的需要。

您可以使用解决方案:

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));
要在
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));

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

 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);
具有静态方法
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);
Stream myStream=Stream.of(“一”、“二”、“三”);
Map result3=Streams.mapWithIndex(myStream,(s,i)->Maps.immutableEntry(i+1,s))
.collect(Collectors.toMap(Map.Entry::getKey,Map.Entry::getValue));
//{1=1,2=2,3=3}
系统输出打印项次(结果3);

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

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));
        });
Map result=Stream.of(“一”、“二”、“三”)
.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;
        });
}
公共静态收集器toIndexMap(){
返回收集器(
HashMap::新建,
(m,s)->m.put(m.size()+1,s),
(m1,m2)->{
如果(m1.isEmpty())返回m2;
如果(!m2.isEmpty()){
int offset=m1.size();
m2.forEach((i,s)->m1.put(i+offset,s));
}
返回m1;
});
}
然后可以像这样使用

Map<Integer,String> result = Stream.of("one", "two", "three")
    .collect(MyCollectors.toIndexMap());
Map result=Stream.of(“一”、“二”、“三”)
.collect(MyCollectors.toIndexMap());

Map result=IntStream.rangeClosed(11000)
.boxed().parallel()
.collect(MyCollectors.toIndexMap());

到目前为止,我对AtomicInteger一无所知。谢谢,这解决了我的问题。
AtomicInteger
是线程安全的,可能会带来一些开销。我认为它有点笨拙。我认为萨缪尔菲利普的解决方案比这更好。还要注意的是,它只解决了变量不是有效最终变量的问题。讽刺的是,由于依赖于处理顺序,它承担了线程安全操作的成本,而不是线程安全操作的成本。如果没有这些问题,这是可能的……我曾认为这是一种解决方法,但希望避免在可能的情况下首先将流转换为列表。无论如何,非常感谢您的回答。我猜索引也是AtomicInteger类型的。感谢您的示例。使用
indexOf
并不是一个好主意,因为有两个原因:对于大型列表,它的时间复杂度为O(n)(对于
ArrayList
),性能较差;如果列表多次包含值,则会得到错误的结果,因为
indexOf
只返回第一个索引,所以您将覆盖第一个值。@SamuelPhilipp您所做的是正确的,即使我已经想到了这一点,所以此解决方案适用于小列表。但是OP特别询问“有没有一种简单的方法将流的元素映射到它们的索引”,所以OP想要列表中的实际索引。如果存在重复项,则元素索引的最后一次出现将添加到映射中。感谢您添加一个mor方法。但是如果我添加了一个重复的字符串“one”,这将产生一个
java.lang.IllegalStateException:Duplicate key one
,但是如果列表包含唯一的元素,它将起作用。@不,是的,我忘了在
toMap
的最后一个参数中添加
mergeFunction
,它解决了重复键的合并错误
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);
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());