21

As for example, there are two lists:

List<Double> list1 = Arrays.asList(1.0, 2.0);
List<String> list2 = Arrays.asList("one_point_zero", "two_point_zero");

Using Stream, I want to create a map composed of these lists, where list1 is for keys and list2 is for values. To do it, I need to create an auxiliary list:

List<Integer> list0 = Arrays.asList(0, 1);

Here is the map:

Map<Double, String> map2 = list0.stream()
                .collect(Collectors.toMap(list1::get, list2::get));

list0 is used in order list1::get and list2::get to work. Is there a simpler way without creation of list0? I tried the following code, but it didn't work:

Map<Double, String> map2 = IntStream
                .iterate(0, e -> e + 1)
                .limit(list1.size())
                .collect(Collectors.toMap(list1::get, list2::get));
Naman
  • 27,789
  • 26
  • 218
  • 353
sva605
  • 1,571
  • 3
  • 20
  • 34
  • The answer is found: I should have used Stream.iterate(0, e -> e + 1) instead of IntStream.iterate(0, e -> e + 1) – sva605 Oct 10 '16 at 17:36
  • 1
    so you can close questions – Sergii Getman Oct 10 '16 at 17:53
  • See also http://stackoverflow.com/questions/17640754/zipping-streams-using-jdk8-with-lambda-java-util-stream-streams-zip – Alexis C. Oct 10 '16 at 21:12
  • 1
    No, *don’t* use `Stream.iterate(0, e -> e + 1).limit(list1.size())`. Use `IntStream.range(0, list1.size()).boxed()`. The combination of `iterate` and `limit` has signifcant performance drawbacks compared to a straight-forward `range` stream. And converting an `IntStream` to a `Stream` is as easy as chaining `boxed()`. You can even avoid boxed `Integer`s entirely: `IntStream.range(0, list1.size()).collect(HashMap::new, (m,i) -> m.put(list1.get(i),list2.get(i)), Map::putAll)` – Holger Oct 11 '16 at 09:53

3 Answers3

26

Instead of using an auxiliary list to hold the indices, you can have them generated by an IntStream.

Map<Double, String> map = IntStream.range(0, list1.size())
            .boxed()
            .collect(Collectors.toMap(i -> list1.get(i), i -> list2.get(i)));
svarog
  • 9,477
  • 4
  • 61
  • 77
10

Indeed the best approach is to use IntStream.range(startInclusive, endExclusive) in order to access to each element of both lists with get(index) and finally use Math.min(a, b) to avoid getting IndexOutOfBoundsException if the lists are not of the exact same size, so the final code would be:

Map<Double, String> map2 = IntStream.range(0, Math.min(list1.size(), list2.size()))
    .boxed()
    .collect(Collectors.toMap(list1::get, list2::get));
Nicolas Filotto
  • 43,537
  • 11
  • 94
  • 122
  • While this answer is essentially the same as @svarog's, I like the use of `Math.min` and the `::get` method references (instead of the lambdas) which look cleaner and safer. – Guss Sep 24 '19 at 10:59
4

This works for me but is O(n^2):

    Map<Double, String> collect =
            list1.stream()
                    .collect(
                            toMap(Double::doubleValue, 
                                    item -> list2.get(list1.indexOf(item))));
Sergii Getman
  • 3,845
  • 5
  • 34
  • 50
  • 1
    This looks workable, but `indexOf` can be very expensive, especially for long lists and lists with complex values (such as `String`). The two top-rated answers are better. – Guss Sep 24 '19 at 10:55