Home > Software engineering >  Java 8 Streams Shallow copy of map object, cross join using streams
Java 8 Streams Shallow copy of map object, cross join using streams

Time:04-08

My inputs are :

List<Map<String, String>> = [{1=a, 2=b, 3=c},
                             {1=d, 2=e, 3=f}]
List<String> = [x, y, z]

Expected Output is :

[
{1=a, 2=b, 3=c, 4=x},
{1=a, 2=b, 3=c, 4=y},
{1=a, 2=b, 3=c, 4=z},
{1=d, 2=e, 3=f, 4=x},
{1=d, 2=e, 3=f, 4=y},
{1=d, 2=e, 3=f, 4=z}
]

My Code :

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class ExplodeData {
    public static void main(String[] args) {
        List<Map<String, String>> listOfMap = List.of(
        new HashMap<>(Map.of("1", "a", "2", "b", "3", "c")),
        new HashMap<>(Map.of("1", "d", "2", "e", "3", "f"))
        );
        List<String> stringList = new ArrayList<>();
        stringList.add("x");
        stringList.add("y");
        stringList.add("z");

        listOfMap.forEach(System.out::println);
        System.out.println(stringList   "\n\n");

        List<Map<String, String>> result = new ArrayList<>();

        for (Map<String, String> eachMap: listOfMap) {
            for (String eachString: stringList) {
                Map<String, String> newEntry = new HashMap<>();
                newEntry.putAll(eachMap);
                newEntry.put("4", eachString);
                result.add(newEntry);
            }
        }
        System.out.println("Expected Result using for loops");
        result.forEach(System.out::println);

        List<Map<String, String>> result2 = stringList
                .stream()
                .flatMap(each -> listOfMap.stream().peek(entry -> entry.put("4", each)))
                .collect(Collectors.toList());

        System.out.println("\n\nResult using streams");
        result2.forEach(System.out::println);
    }
}

Problem: I want to convert my for loop which gives the correct result into stream. My current stream code gives the correct size of result i.e. (listOfMap * stringList) but the value for the new key is overridden due to shallow copy.

Current output

Expected Result using for loops
{1=a, 2=b, 3=c, 4=x}
{1=a, 2=b, 3=c, 4=y}
{1=a, 2=b, 3=c, 4=z}
{1=d, 2=e, 3=f, 4=x}
{1=d, 2=e, 3=f, 4=y}
{1=d, 2=e, 3=f, 4=z}


Result using streams
{1=a, 2=b, 3=c, 4=z}
{1=d, 2=e, 3=f, 4=z}
{1=a, 2=b, 3=c, 4=z}
{1=d, 2=e, 3=f, 4=z}
{1=a, 2=b, 3=c, 4=z}
{1=d, 2=e, 3=f, 4=z}

Your help is appreciated.

CodePudding user response:

You can use flatMap() with a function that generates a stream of new maps, much like the loop version does, and collect everything back into a list. Your stream version modifies existing maps in-place, and keeps overwriting previously added "4" elements with new ones.

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class Demo {
    public static void main(String[] args) {
        List<Map<String, String>> listOfMap =
            List.of(Map.of("1", "a", "2", "b", "3", "c"),
                    Map.of("1", "d", "2", "e", "3", "f"));
        List<String> stringList = List.of("x", "y", "z");

        List<Map<String, String>> result =
            listOfMap.stream()
            .flatMap(map -> stringList.stream().map(elem -> {
                        Map<String, String> newMap = new HashMap<>(map);
                        newMap.put("4", elem);
                        return newMap;
                    }))
            .collect(Collectors.toList());

        for (Map<String, String> elem : result) {
            System.out.println(elem);
        }
    }
}

outputs

{1=a, 2=b, 3=c, 4=x}
{1=a, 2=b, 3=c, 4=y}
{1=a, 2=b, 3=c, 4=z}
{1=d, 2=e, 3=f, 4=x}
{1=d, 2=e, 3=f, 4=y}
{1=d, 2=e, 3=f, 4=z}

CodePudding user response:

Here is one way which avoids using flatMap.

List<Map<String, String>> map =
        List.of(Map.of("1", "a", "2", "b", "3", "c"),
                Map.of("1", "d", "2", "e", "3", "f"));
List<String> list = List.of("x", "y", "z");
  • use mapmulti to create a new map and modify it with the new elements
  • then put each new map on the stream
  • and collect into a list.
List<Map<String, String>> newList = map.stream()
        .<Map<String, String>>mapMulti((mp, consumer) -> {
            for (String v : list) {
                Map<String, String> newMap =
                        new HashMap<>(mp);
                newMap.put("4", v);
                consumer.accept(newMap);
            }
        }).toList();

for (Map<?,?> nmap : newList) {
    System.out.println(nmap);
}

prints

{1=a, 2=b, 3=c, 4=x}
{1=a, 2=b, 3=c, 4=y}
{1=a, 2=b, 3=c, 4=z}
{1=d, 2=e, 3=f, 4=x}
{1=d, 2=e, 3=f, 4=y}
{1=d, 2=e, 3=f, 4=z}

mapMulti was introduced in Java 16.

  • Related