Duta
Duta

Reputation: 19

Java: join two lists of Map<String, Object> using Stream

I have two lists of Map<String, Object> as shown below:

List1=[ {ID=1, actor="A", film="AA"}, 
        {ID=1, actor="B", film="AA"} ]

List2={ [ID = 1, director="C"] } 

Result = { [ID=1, actor="A", film="AA", director="C"], 
           [ID=1, actor="B", film="AA", director="C"] }

I want to use the Stream class in Java 8 to join these lists.

How do I join the to get the value of Result shown?

Is the Stream class in Java 8 fast and stable if List1 and List2 are very big?

Upvotes: 1

Views: 2627

Answers (2)

steffen
steffen

Reputation: 16938

If performance matters, you should first build an index of directors:

Map<Object, Object> directors = list2.stream()
    .collect(Collectors.toMap(m -> m.get("ID"), m -> m.get("director")));

Then you can merge the directors to the list entries easily:

list1.stream().forEach(m -> m.put("director", directors.get(m.get("ID"))));

Accesing the director via a Map will be faster than searching the director for each list entry.

Upvotes: 1

name not found
name not found

Reputation: 622

Ah now I understand what you want :) I don't know if there is a better way with streams but here is a solution which would work.

List<Map<String, String>> resultList = l1.stream()
        .map(m1 -> {
           Map<String, String> map = new HashMap<>();
           map.putAll(m1);
           l2.stream()
               .filter(m2 -> map.get("ID").equals(m2.get("ID")))
               .findFirst()
               .ifPresent(m2 -> map.put("director", m2.get("director")));
           return map;
        })
        .collect(Collectors.toList());

The above code generates a new List resultList and does not modify the other lists List1 and List2. If it does not matter if List1 gets modified or not you could do it in a cleaner, more readable way.

l1.forEach(m1 -> l2.stream()
    .filter(m2 -> m1.get("ID").equals(m2.get("ID")))
    .findFirst()
    .ifPresent(m2 -> m1.putIfAbsent("director", m2.get("director"))));

This way the entries of list1 get modified. So with the above example list1 is becomes the joined list. But it's actually good practice to have methods without any side effects. So I would not prefer the above example.

I would recommend a method getJoinedList which returns a new List and does not modify the other lists. And in this case I would not use streams but the old-fashioned for-loop.

private static List<Map<String, String>> getJoinedList(
    List<Map<String, String>> l1, List<Map<String, String>> l2) {

    List<Map<String, String>> result = new ArrayList<>();
    for (Map<String, String> m1 : l1) {
        Map<String, String> newMap = new HashMap<>();
        newMap.putAll(m1);
        for (Map<String, String> m2 : l2) {
            if (m1.get("ID").equals(m2.get("ID"))) {
                newMap.put("director", m2.get("director"));
                break;
            }
        }
        result.add(newMap);
    }

    return result;
}

Then you just can call the method like this.

List<Map<String, String>> joinedList = getJoinedList(l1, l2);

Upvotes: 1

Related Questions