javalistjava-streamjava-11

Filtering/removing from a nested List of Objects using streams in Java


Say that we have a 3-dimensional List of Objects:

class OneDObject {
  int id;
  List<Integer> list;
  OneDObject(int id, List<Integer>list) { /* Constructor */ }
  // Getters and Setters
}

class TwoDObject {
  int id;
  List<OneDObject> list;
  TwoDObject(int id, List<OneDObject> list) { /* Constructor */ }
  // Getters and Setters
}

var l1 = List.of(1,2,4);
var l2 = List.of(2,4,6);
var obj1d1 = new OneDObject(1, l1);
var obj1d2 = new OneDObject(2, l2);
var l3 = List.of(obj1d1, obj1d2);
var l4 = List.of(obj1d1);
var obj2d1 = new TwoDObject(3, l3);
var obj2d2 = new TwoDObject(4, l4);
var l5 = List.of(obj2d1, obj2d2);   // 3-d list

Say that I want to filter "l5" such that if any element in the inner most list is an odd number then the entire list should be deleted, and if that makes the 2nd level list as empty, then that should be deleted in return.

So, for the given example, before filtering if it is:

[[[1,2,4],[2,4,6]], [[1,2,4]]]

After filtering, it should be:

[[[2,4,6]]]

How can I do this using streams in Java?


Solution

  • Since you need your lists to be updated, in the below solution I am using removeIf method of the List to remove any elements which does not meet the necessary criteria. So for removeIf to work, the list should not be immutable. So replace the var list = List.of(...) code with var list = new ArrayList<>(List.of(...)); (Note: Null checks have been ignored as well.)

    Now, this problem could be split into components:

    1. Predicate to identify if a list has any odd elements.
    Predicate<OneDObject> hasOdd = obj-> obj.getList().stream().anyMatch(i -> i % 2 != 0);
    
    1. Predicate to remove objects from 2d list, which has odd elements in its 1d list.
    Predicate<TwoDObject> validate2d = obj -> {
        // remove any 1d list that has atleast one odd number.
        obj.getList().removeIf(hasOdd);
        // check if there are any valid 1d lists
        return obj.getList().isEmpty();
    };
    
    1. Now apply the predicate to the final list:
    l5.removeIf(validate2d); // l5 will now contain only the 2d object having [2,4,6] list