S K
S K

Reputation: 61

How to use a stream to create a map?

Input: List<Foo> rawdata

Desired Output: Map<Foo, Bar>

Method implementation: Java 8

public Map<Foo, Bar> getMap(List<Foo> rawdata) {
    rawData.stream().flatmap(d -> {
        val key = method1(d);
        val value = method2(d);
    })
    // Now I want to create a map using the Key/Value
    // pair which was obtained from calling method 1 and 2.
}

I did try this:

public Map<Foo, Bar> getMap(List<Foo> rawdata) {
    rawData.stream()
        .flatmap(
            d -> {
                val key = method1(d);
                val value = method2(d);
                return Stream.of(new Object[][]{key, value});
            })
        .collect(Collectors.toMap(
            key -> key[0],
            key -> key[1]));
}

However want to see if there is less expensive way of doing this or any better way.

Upvotes: 1

Views: 1087

Answers (2)

user14940971
user14940971

Reputation:

You can use toMap(keyMapper,valueMapper,mergeFunction) method with three parameters that allows to merge duplicates into a list, for example:

List<String[]> list = List.of(
        new String[]{"Foo1", "Bar1"},
        new String[]{"Foo1", "Bar2"},
        new String[]{"Foo2", "Baz"});

Map<String, List<String>> map1 = list.stream()
        .collect(Collectors.toMap(
                e -> e[0],
                e -> new ArrayList<>(List.of(e[1])),
                (list1, list2) -> {
                    list1.addAll(list2);
                    return list1;
                }));

System.out.println(map1);
// {Foo1=[Bar1, Bar2], Foo2=[Baz]}

If you are sure that there are no duplicates, you can use toMap(keyMapper,valueMapper) method with two parameters:

List<String[]> list = List.of(
        new String[]{"Foo1", "Bar1"},
        new String[]{"Foo2", "Baz"});

Map<String, String> map2 = list.stream()
        .collect(Collectors.toMap(e -> e[0], e -> e[1]));

System.out.println(map2);
// {Foo1=Bar1, Foo2=Baz}

Anyway you can collect a list of Map.Entrys:

List<String[]> list = List.of(
        new String[]{"Foo1", "Bar1"},
        new String[]{"Foo1", "Bar2"},
        new String[]{"Foo2", "Baz"});

List<Map.Entry<String, String>> list1 = list.stream()
        .map(arr -> Map.entry(arr[0], arr[1]))
        .collect(Collectors.toList());

System.out.println(list1);
// [Foo1=Bar1, Foo1=Bar2, Foo2=Baz]

See also: Ordering Map<String, Integer> by List<String> using streams

Upvotes: 0

Robby Cornelissen
Robby Cornelissen

Reputation: 97150

There's no point in creating an intermediate stream and then using flatMap().

You can just collect() directly into a map:

public Map<Foo, Bar> getMap(List<Foo> rawData) {
    return rawData.stream()
            .collect(Collectors.toMap(foo -> method1(foo), foo -> method2(foo)));
              // OR: Collectors.toMap(this::method1, this::method2);
}

Upvotes: 5

Related Questions