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
  • 274,122
  • 60
  • 696
  • 724
Cael
  • 556
  • 1
  • 10
  • 36
  • 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
  • Stream may be a good hammer, but not all problems are nails. IMO using nested for-loop is the simplest and cleanest way to achieve what you want. – samabcde Jul 09 '22 at 13:25

6 Answers6

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
  • 44,416
  • 5
  • 53
  • 99
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
  • 2,572
  • 13
  • 14
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,393
  • 1
  • 8
  • 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
  • 4,332
  • 3
  • 26
  • 44
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
  • 2,137
  • 4
  • 13
0

Try using stream() along with map() and flatMap() like so:

Iterator<Integer> itr11 = val1.stream().flatMap(l1 -> l1.stream()).collect(Collectors.toList()).iterator();
List<List<Integer>> ans = val2.stream().map(list11 -> list11.stream().map(i -> i + itr11.next()).collect(Collectors.toList())).collect(Collectors.toList());
ans.stream().forEach(System.out::print);

More info on the flatMap() method here: https://www.javatpoint.com/flatmap-method-in-java-8

flyingfishcattle
  • 1,817
  • 3
  • 14
  • 25