3

I have 2 list of lists as below:

List<List<Integer>> val1 : [[2, 3, 0], [1, 3, 5], [0, 2, 4]]
List<List<Integer>> val2 : [[0, 3, 1], [3, 1, 4], [1, 1, 2]]

I wish to compute a new list of lists (sum) that has the sum of corresponding index from the two lists above.

List<List<Integer>> sum  : [[2, 6, 1], [4, 4, 9], [1, 3, 6]]

Is there a better way of doing this using Java 8? Currently I'm using a for loop to achieve this and it doesn't look nice.

   for (int rowIdx = 0; rowIdx < val1.size(); rowIdx++) {
    for (int colIdx = 0; colIdx < val2.get(0).size(); colIdx++) {
     final int cumSum = val1.get(rowIdx).get(colIdx) + val2.get(rowIdx).get(colIdx);
     sum.get(rowIdx).set(colIdx, cumSum);
     }
   }
Sotirios Delimanolis
  • 263,859
  • 56
  • 671
  • 702
Cael
  • 556
  • 1
  • 10
  • 34
  • 2
    [Related](https://stackoverflow.com/questions/17640754/zipping-streams-using-jdk8-with-lambda-java-util-stream-streams-zip). – Sweeper May 13 '21 at 05:01

5 Answers5

1

Here's something that does what you're looking for (with a lot of assumptions) using the stream API. You judge for yourself whether that looks better:

List<List<Integer>> sum = IntStream.range(0, val1.size())
        .mapToObj(i -> IntStream.range(0, val1.get(i).size())
                       .mapToObj(e -> val1.get(i).get(e) + val2.get(i).get(e))
                .collect(Collectors.toList()))
        .collect(Collectors.toList());

// results in [[2, 6, 1], [4, 4, 9], [1, 3, 6]]

That does the same thing as your loop (except for the potentially buggy colIdx < val2.get(0).size()).

Assumptions:

  • val1 and val2 have the same size
  • val1 and val2 have identically sized nested lists, element-wise
  • none of your Integer objects is null
ernest_k
  • 42,928
  • 5
  • 50
  • 93
1

with the assumptions made – val1 and val2 and every list in val1 and val2 has the same size – you can add the integer values directly

Stream<List<Integer>> s1 = val1.stream();
Stream<List<Integer>> s2 = val2.stream();

Iterator<Integer> it = s2.flatMap(List::stream).iterator();
List<List<Integer>> val3 = s1.map(l -> l.stream().map(i -> i + it.next())
    .collect(toList())).collect(toList());
Kaplan
  • 1,266
  • 7
  • 7
0

StreamEx can be used here:

public class Test {
    public static void main(String[] args) {
        var listOne = List.of(List.of(1,2), List.of(3,4), List.of(10, 12));
        var listTwo = List.of(List.of(10,12), List.of(13,24), List.of(10, 2));
        StreamEx.zip(listOne , listTwo, Test::zipBy).forEach(System.out::println);
    }
    
    public static List<Integer> zipBy(List<Integer> _1, List<Integer> _2) {
        return StreamEx.zip(_1, _2, Integer::sum).toList();
    }
}

Output:

[11, 14]
[16, 28]
[20, 14]

EDIT:

The code above can be made java-8 compatible by updating the list generation construct to:

List<List<Integer>> listOne = Arrays.asList(Arrays.asList(1,2), Arrays.asList(3,4),Arrays.asList(10, 12));
List<List<Integer>> listTwo =Arrays.asList(Arrays.asList(10,12), Arrays.asList(13,24), Arrays.asList(10, 2));
adarsh
  • 1,295
  • 1
  • 7
  • 16
0

You can do something like this. I would love Java to have something like Reactor's zip operator to handle such scenarios:

List<Integer> zippedList = IntStream.range(0, list1.size())
                                    .map(idx -> zipLists(list1.get(idx), list2.get(idx)))
                                    .collect(toList());

private List<Integer> zipLists(List<Integer> l1, List<Integer> l2) {
    return IntStream.range(0, l1.size())
                    .map(idx -> l1.get(idx) + l2.get(idx))
                    .collect(toList());
}
Prashant Pandey
  • 3,726
  • 2
  • 19
  • 36
0

Assuming that outer and inner lists are the same length and you know size of inner Lists, you can collect to a new List by using stream of indexes of all inner elements and for each of them calculate index of it's row and column:

int colCnt = 3;

List<List<Integer>> sum =
    IntStream.rangeClosed(0, val1.size() * colCnt-1)
             .collect(ArrayList::new,
                      (list, elNum) -> { int rowIdx = elNum / colCnt;
                                         int colIdx = elNum % colCnt;
                                         if (colIdx == 0) list.add(rowIdx, new ArrayList<>());
                                         list.get(rowIdx)
                                             .add(colIdx, val1.get(rowIdx).get(colIdx) +
                                                          val2.get(rowIdx).get(colIdx));
                                       },
                      List::addAll);
Sergey Afinogenov
  • 1,856
  • 3
  • 10