How to map elements to their index using streams?

You can use an IntStream to solve this:

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

You create an IntStream from 0 to list.size() - 1 (IntStream.range() excludes the last value from the stream) and map each index to the value in your list. The advantage of this solution is, that it will also work with parallel streams, which is not possible with the use of an AtomicInteger.

So the result in this case would be:

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

To start the first index at 1 you can just add 1 during collect:

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

This will result in this:

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

Guava has a static method 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);

A clean solution not requiring random access source data, is

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));
        });

This also works with parallel streams.

In the unlikely case that this is a recurring task, it’s worth putting the logic into a reusable collector, including some optimizations:

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;
        });
}

Which can then be used like

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

or

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

You i variable is not effectively final.

You can use AtomicInteger as Integer wrapper:

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()));

I consider it a bit hacky because it only solves the problem of effectively final variable. Since it is a special ThreadSafe version it might introduce some overhead. Pure stream solution in the answer by Samuel Philipp might better fit your needs.