1

Am trying to convert List<Integer[]> to Map<Integer, List<Integer>>.

So this List<Integer[]> has duplicate keys for an example see below:

List<Integer[]> list = Arrays.asList(
    new Integer[] {1, 23},
    new Integer[] {2, 45},
    new Integer[] {3, 25},
    new Integer[] {1, 45},
    new Integer[] {2, 54}
);

The requirement is to convert this in to below map where it groups by the 0th index and put all the values under duplicate key again to a sub list. So finally it would be something like this:

{1=[23, 45], 2=[45, 54], 3=[25]}

I am currently doing this without streams like below. And am not sure how it can be achieved with stream.

if (finalMap.containsKey(Integer.valueOf(dataList[0]))){
    List<Integer> currentList = finalMap.get(Integer.valueOf(dataList[0]));
    currentList.add(new Integer(Integer.valueOf(dataList[1])));
    List<Integer> newList = new ArrayList<>(currentList);
    finalMap.put(Integer.valueOf(dataList[0]), newList);
} else {
    finalMap.put(
        Integer.valueOf(dataList[0]), 
        new ArrayList<>(Arrays.asList(Integer.parseInt(dataList[1]))));
}
Nikolas Charalambidis
  • 40,893
  • 16
  • 117
  • 183

3 Answers3

4

Here is the solution without using any 3rd party API and using streams.

        Integer [] array1 = {1, 23};
        Integer [] array2 = {2, 45};
        Integer [] array3 = {3, 25};
        Integer [] array4 = {1, 45};
        Integer [] array5 = {2, 54};
        List<Integer[]> intlist = Arrays.asList( array1,array2,array3,array4,array5);
        
        Map<Integer, List<Integer>> grouped = intlist.stream()
                .collect(Collectors.groupingBy(arr -> arr[0], 
                         Collectors.mapping(arr -> arr[1], Collectors.toList())));
        
        System.out.println( grouped );

Will produce

{1=[23, 45], 2=[45, 54], 3=[25]}

as required.

Jabir
  • 2,776
  • 1
  • 22
  • 31
3

With Streams:

Map<Integer, List<Integer>> map =
        dataList.stream().collect(Collectors.toMap(i -> i[0], i -> List.of(i[1]), (a, b) -> {
            List<Integer> merged = new ArrayList<>(a);
            merged.addAll(b);
            return merged;
        }));

For readability I would recommend the merge operation to be moved to a separate method. You could also try achieving the same result with groupingBy but this is a bit trickier to get right when using arrays.

Jeroen Steenbeeke
  • 3,884
  • 5
  • 17
  • 26
2

You can achieve this using Collectors.groupingBy by the first element of the array and use a mapping collector as a downstream to get the further values. Finally, the further downstream Collectors.toList wraps the values. Use AbstractMap.SimpleEntry for better manipulation with the array items.

Now, the solution only depends, whether each Integer[] contains exactly 2 items or 2+ items:


Exactly 2 items in each array

Each Integer[] always contain exactly two items (ex. new Integer[] {1, 23}). Use Collectors.mapping to get the array[1] (entry's value) result into the List.

Map<Integer, List<Integer>> map = list.stream()
    .map(array -> new AbstractMap.SimpleEntry<>(array[0], array[1]))
    .collect(Collectors.groupingBy(
            AbstractMap.SimpleEntry::getKey, 
            Collectors.mapping(AbstractMap.SimpleEntry::getValue, Collectors.toList())));

Actually, the SimpleEntry usage can be omitted and the whole stream simplified if you address the arrays directly:

Map<Integer, List<Integer>> map = list.stream()
    .collect(Collectors.groupingBy(
            array -> array[0],
            Collectors.mapping(array -> array[1], Collectors.toList())));

2+ items in each array

Each Integer[] can contain two or more items (ex. new Integer[] {1, 23, 24}). Use Collectors.flatMapping to get the array[1] .. array[n] items (entry's value) into the List. Remember, you need to split the array first to perform the correct grouping, therefore the first element array[0] is the key and array[1] .. array[n] are values - that's why flatmapping is required.

Map<Integer, List<Integer>> map = list.stream()
    .map(array -> new AbstractMap.SimpleEntry<>(
            array[0], 
            Arrays.copyOfRange(array, 1, array.length)))
    .collect(Collectors.groupingBy(
            AbstractMap.SimpleEntry::getKey,
            Collectors.flatMapping(
                    i -> Arrays.stream(i.getValue()), 
                    Collectors.toList())));

Again, the whole stream can be simplified omitting the SimpleEntry wrapper:

Map<Integer, List<Integer>> map = list.stream()
    .collect(Collectors.groupingBy(
            array -> array[0],
            Collectors.flatMapping(
                    array -> Arrays.stream(Arrays.copyOfRange(array, 1, array.length)),
                    Collectors.toList())));

Consequently, thanks to geting a subarray which can result in an empty array, this solution also works when there is at least one element present in the Integer[] array. A result with the array new Integer[] {3} in the input would be:

{1=[23, 45], 2=[45, 54, 88], 3=[]}

Nikolas Charalambidis
  • 40,893
  • 16
  • 117
  • 183