2

I have the following classes.

Class A {
    List<B> b
   //getters and setters
}

CLass B {
   List<C> c
   //getters and setters
}

 Class C {
    List<D> d
   //getters and setter
}

Class D {}

What i want to do is remove list d if a specific search term is not in the list. I have tried to do it but no luck. I think it removes but the reference is not saved.

a.stream()
        .flatMap(a-> a.getB().stream())
        .flatMap(b-> b.getC().stream())
        .map(c-> c.getD())
        .collect(Collectors.toList())
        .removeIf(list -> {
            boolean toBeRemoved = true;
            boolean containsMatch = list.stream().anyMatch(w-> {return w.getId().equalsIgnoreCase(searchTerm);});
            if(containsMatch) {
                toBeRemoved = false;
            }               
            return toBeRemoved;
        });

Can someone help me?

Stefan Zobel
  • 3,182
  • 7
  • 28
  • 38
Nana
  • 55
  • 8
  • To be clear: you want to remove the elements from the stream, or from the real list?! Because the later isn't possible this way. Operations on the stream of `a` do not change `a` itself! – GhostCat Jul 12 '18 at 07:21
  • from the real list. The real list here is "a" which is of type List – Nana Jul 12 '18 at 07:22
  • So object 'c' will have an empty list ? If the searchItem is found in its list ? – azro Jul 12 '18 at 07:26
  • Yes Azro, exactly. – Nana Jul 12 '18 at 07:28
  • 1
    Just a side note, instead of `boolean toBeRemoved = true; boolean containsMatch = someExpression; if(containsMatch) { toBeRemoved = false; } return toBeRemoved;`, you can simply write `return ! someExpression;` combine this with the fact that instead of `parameter -> { return expression; }`, you can write `parameter -> expression`… – Holger Jul 12 '18 at 08:47

2 Answers2

2

A stream represents a view on the "underlying" collection. This means that when you call removeIf() on the stream, the "underlying" collection isn't affected at all.

You would need to do two things: first you "collect" all items you intend to delete, and then you simply remove them (in an explicit call) from the list that needs to be changed:

List<B> toBeDeleted = a.stream()....collect(Collectors.toList());
a.b.removeAll(toBeDeleted);

( the above is meant as pseudo code, I didn't run it through the compiler )

As said: the real problem here is your misconception: operations on the stream normally do not affect the underlying collection.

GhostCat
  • 137,827
  • 25
  • 176
  • 248
  • 1
    Almost the perfect answer though just a side note. "_This means that when you call removeIf() on the stream"_. a stream does not have a `removeIf` method rather `removeIf` belongs to anything of type `Collection`. "_operations on the stream normally do not affect the underlying collection._". Although it might be true for this specific case, stream elements are not always stored in an underlying collection as sometimes they may be generated on demand. – Ousmane D. Jul 12 '18 at 19:07
1

What you did builds a List<List<D>> and you remove List<D> elements that does not correponds, but that never changes the objects you have.

  • You need to iterate over all C elements,
  • You keep the ones that does not correpond (use noneMatch() to check this)
  • for these ones you replace the list by an empty one (or clear the actual c.getD().clear())

a.stream()
    .flatMap(a-> a.getB().stream())
    .flatMap(b-> b.getC().stream())
    .filter(c -> c.getD().stream().noneMatch(w -> w.getId().equalsIgnoreCase(searchTerm)))
    .forEach(c-> c.setD(new ArrayList<>()));    // or .forEach(c-> c.getD().clear());
azro
  • 53,056
  • 7
  • 34
  • 70