4

Is there a convenient Java8 stream API way to convert from List<T> to Map<T, (index)>like below example:

    List<Character> charList = "ABCDE".chars().mapToObj(e->(char)e).collect(Collectors.toList());

    Map<Character, Integer> map = new HashMap<>();
    for (int i = 0; i < charList.size(); i++) {
        map.put(charList.get(i), i);
    }

map = {A=0, B=1, C=2, D=3, E=4}

Daniel Hári
  • 7,254
  • 5
  • 39
  • 54
  • 1
    Streams API seems to be badly missing the `zipWithIndex` operation. – Marko Topolnik Oct 29 '16 at 12:49
  • Don't think it's a duplicate, but you might use a collector like this: https://gist.github.com/lyubomyr-shaydariv/ab8ef4c00ed2e0e0ae9581af0f97f9b5 – Lyubomyr Shaydariv Oct 29 '16 at 13:08
  • @LyubomyrShaydariv: Thanks, looks fine, but it's far more complex than the original. The target is to make it simple, readable. – Daniel Hári Oct 29 '16 at 13:11
  • @DanielHári Well, collector implementations are usually complex, but written once, so you can use them anywhere. – Lyubomyr Shaydariv Oct 29 '16 at 13:15
  • 1
    In this question, the goal is to iterate over characters in a String, with their index. But the Stream API doesn't have a good way to have a Stream with the chars and their indexes, and the linked question shows how to do that in a general purpose way: create an `IntStream` and then maybe call `mapToObj` (or `boxed()`). – Tunaki Oct 29 '16 at 21:39
  • What’s the purpose of this `Map`? – Holger Oct 31 '16 at 09:18
  • @Tunaki I think this question is not a duplicate, on the contrary, I exactly need a Map because I need fast access to the index starting from an instance of T. The linked question does not address this use case. – Jako Sep 23 '19 at 17:29

1 Answers1

8

You can use the following nasty trick, but it's not elegant, and not efficient at all on linked lists:

List<String> list = Arrays.asList("a", "b", "c");
Map<String, Integer> result = 
    IntStream.range(0, list.size())
             .boxed()
             .collect(Collectors.toMap(list::get, Function.identity()));

It's also less readable than the simple for loop, IMO. So I would stick to that.

JB Nizet
  • 678,734
  • 91
  • 1,224
  • 1,255
  • Or skip the `List` completely, and use the original string, where `String::charAt` is the key supplier. – Mureinik Oct 29 '16 at 12:57
  • 3
    Sorry, that wasn't clear enough. The List is irrelevant. You can do that on any list of any kind. My answer only focused on how to translate a list to a map. Not on the String characters. – JB Nizet Oct 29 '16 at 12:59
  • I don’t see why this is a “nasty trick”. It’s the canonical solution for index based operations. – Holger Oct 31 '16 at 09:18
  • @Holger Yes, but iterating through a list shouldn't be an index-based operation: it's very inefficient on non-random-access lists. It also feels unnatural not to start with list.something(), but with an Intstream. – JB Nizet Oct 31 '16 at 16:09
  • The unnatural part is given by the task, which, for whatever reason, depends on the index. If the task demands the index, streaming over the indices is not unnatural compared to streaming over the elements. The problems start with combining characters or code points outside the BMP but, as said, they lie already in the task itself and are only inherited by the solution. – Holger Oct 31 '16 at 17:27
  • I find this equivalent Kotlin code much more natural, although I use Java much more than Kotlin: https://gist.github.com/jnizet/39fe9e1801cb969143302491d8593678. – JB Nizet Oct 31 '16 at 17:40