简体   繁体   中英

Removing elements of one list if present in another list using stream

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

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())
    )
);

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.

The recommended solution is to override toString and hasCode() methods in the class, Car and then you can, as already comment below the question, do something like

cars1.removeAll(cars2);

or

cars1.removeIf(cars2::contains);

However, if these two methods are not overridden in the class, Car , you will have to compare each attribute within the nested loops. This approach is only useful to learn nested loops and is never recommended for production use.

Demo:

import java.util.ArrayList;
import java.util.List;

class Car {
    int id;
    String name;

    @Override
    public String toString() {
        return "Car [id=" + id + ", name=" + name + "]";
    }
}

public class Main {
    public static void main(String[] args) {
        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);

        for (int i = 0; i < cars1.size(); i++) {
            Car car1 = cars1.get(i);
            for (int j = 0; j < cars2.size(); j++) {
                Car car2 = cars2.get(j);
                if (car2.id == car1.id && car2.name.equals(car1.name))
                    cars1.remove(i);
            }
        }

        System.out.println(cars1);
    }
}

Output:

[Car [id=1, name=C1]]

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM