-1

I didn't find any thread here on this Question. I am trying to Remove elements (Cars in my case) of one list (cars1) if present in another list (cars2) using java stream. I tried using removeIf but then it felt like it works more appropriately with List of Strings, etc.

    Car c1 = new Car();
    c1.id = 1;
    c1.name = "C1";

    Car c2 = new Car();
    c2.id = 2;
    c2.name = "C2";

    List<Car> cars1 = new ArrayList<Car>();
    cars1.add(c1);
    cars1.add(c2);

    List<Car> cars2 = new ArrayList<Car>();
    cars2.add(c2);

    // TODO : Remove all the cars from cars1 list that are in cars2 list using java streams
shmosel
  • 49,289
  • 6
  • 73
  • 138
Tiya
  • 553
  • 8
  • 26

2 Answers2

8

If methods hashCode and equals are properly implemented in class Car, the stream-based solutions may look as follows:

  • filter out the values, collect into a new list
// Predicate.not added in Java 11
List<Car> notJava11 = cars1.stream()
                        .filter(Predicate.not(cars2::contains))
                        .collect(Collectors.toList());

List<Car> notIn2 = cars1.stream()
                        .filter(car -> !cars2.contains(car))
                        .collect(Collectors.toList());

  • Use forEach for cars2 (affecting cars1):
cars2.forEach(cars1::remove); 
// no need to call cars2.stream().forEach(cars1::remove);

Here the first occurrence of Car instance is removed in cars1

  • removeIf should work also
cars1.removeIf(cars2::contains);

If you due to some reason equals/hashCode are not overridden in class Car, the following solution may be offered:

List<Car> notIn2 = cars1
        .stream()
        .filter(c1 -> cars2
            .stream()
            .noneMatch(c2 -> 
                 c1.getId() == c2.getId()
                 && Objects.equals(c1.getName(), c2.getName())
            )
        )
        .collect(Collectors.toList());
  • removeIf:
cars1.removeIf(c1 -> cars2
    .stream()
    .anyMatch(c2 -> c1.getId() == c2.getId() 
        && Objects.equals(c1.getName(), c2.getName())
    )
);
Nowhere Man
  • 19,170
  • 9
  • 17
  • 42
  • What if hashCode and equals are not properly implemented or the objects in Cars1 list and cars2 list are not the same but their values (id and name) are same – Tiya Oct 21 '21 at 21:23
  • In your example the same instance `c2` is added to both lists so in such case removal takes place successfully. But assuming that a new object `c2_1` is created instead having the same field values `Car c2_1 = new Car(2, "C2");` -- with default implementation of `equals` `c2.equals(c2_1)` returns `false` because these are [different references](https://stackoverflow.com/questions/4178997/how-default-equals-and-hashcode-will-work-for-my-classes) – Nowhere Man Oct 21 '21 at 21:33
  • True. c2.equals(c2_1) returns false, but what if we want to compare elements (id and name) and then decide if the elements match then car object should be removed from the list? – Tiya Oct 21 '21 at 21:35
  • @Tiya, you may check the update – Nowhere Man Oct 21 '21 at 22:02
  • Thanks for your answer. It works like a charm! – Tiya Oct 22 '21 at 19:00
0

It can be done simply by using removeAll() method. You have to implement hashcode and equals in Car class. Then you can cars1.removeAll(cars2);. this statement would leave only c1 in cars1 list.

gdogra
  • 130
  • 2
  • 12