user2254180
user2254180

Reputation: 856

Streaming and filtering by a sub class

I am trying to use Java streams to filter out a base vehicle class that has car, motorbike, etc. sub classes. There is a parameter called vehicleType that can be CAR, MOTORBIKE, etc. I am trying to create a streaming filter in such a way that List<BaseVehicleEntity<?>> vehicleEntities contains only cars if the vehicleType is CAR, or if vehicleType is anything else, I filter out everything that ISN'T a CAR.

I have tried the following code, but it isn't working. My compiler gives a warning that the second .filter() is being ignored, so I am doing something wrong.

List<BaseVehicleEntity<?>> vehicleEntities = findTasks();

if (vehicleType.equals("CAR")) {
    vehicleEntities.stream()
            .filter(obj -> obj instanceof CarEntity)
            .map(obj -> (CarEntity) obj)
            .filter(obj -> obj.getVehicleType().equals("CAR"));
} else vehicleEntities.stream()
        .filter(obj -> obj instanceof CarEntity)
        .map(obj -> (CarEntity) obj)
        .filter(obj -> !obj.getVehicleType().equals("CAR"));

Upvotes: 1

Views: 339

Answers (4)

Nowhere Man
Nowhere Man

Reputation: 19565

As mentioned earlier, a terminal operation such as collect(Collectors.toList()) needs to be added to return the filtered list.

The requested functionality may be implemented using a separate Predicate to filter the cars depending on vehicleType as follows:

public static List<BaseVehicleEntity> filterByCar(String vehicleType, List<BaseVehicleEntity> vehicles) {

    Predicate<CarEntity> condition = (car) -> "CAR".equals(car.getVehicleType());
    if (!"CAR".equals(vehicleType)) {
        condition = condition.negate();
    }
    return vehicles.stream()
            .filter(v -> v instanceof CarEntity)
            .map(CarEntity.class::cast)
            .filter(condition)
            .collect(Collectors.toList());
}

Example test (LightCar and HeavyCar extend CarEntity, HeavyCar has vehicle type = "SUV"):

System.out.println(filterByCar("CAR", Arrays.asList(
        new CarEntity(), new MotorbikeEntity(), new LightCar(), new HeavyCar())));

System.out.println(filterByCar("NOTCAR", Arrays.asList(
        new CarEntity(), new MotorbikeEntity(), new LightCar(), new HeavyCar())));

Output:

[CarEntity: CAR, LightCar: CAR]
[HeavyCar: SUV]

Upvotes: 1

Nikos Paraskevopoulos
Nikos Paraskevopoulos

Reputation: 40308

First and foremost, a stream must have a terminal operator to actually do anything; terminal operators are e.g. forEach or collect. Notice the Javadocs, they mention that these are terminal operators explicitly. So, your code will actually do nothing until you collect it to a List (if a List is what you want - there are collectors for many standard collection types, e.g. toSet):

List<CarEntity> cars = vehicleEntities.stream()
            .filter(obj -> obj instanceof CarEntity)
            .map(obj -> (CarEntity) obj)
            .filter(obj -> obj.getVehicleType().equals("CAR"))
            .collect(Collectors.toList());

Also consider the partitioningBy collector, which will split the list according to your predicate in a single traversal.

Upvotes: 0

ZhenyaM
ZhenyaM

Reputation: 686

I think issue is that you doesn't collect your filtered collection. filter and map not terminal operations in stream api. So I could suggest next thing:

List<BaseVehicleEntity<?>> vehicleEntities = findTasks();
if (vehicleType.equals("CAR")) {
    List<CarEntity> cars = vehicleEntities.stream()
            .filter(obj -> obj instanceof CarEntity)
            .map(obj -> (CarEntity) obj)
            .filter(obj -> obj.getVehicleType().equals("CAR"))
            .collect(Collectors.toList());
} else {
    List<CarEntity> notCars = vehicleEntities.stream()
        .filter(obj -> obj instanceof CarEntity)
        .map(obj -> (CarEntity) obj)
        .filter(obj -> !obj.getVehicleType().equals("CAR"))
        .collect(Collectors.toList())
}

More information could be found here

Upvotes: 0

GianniCanneloni
GianniCanneloni

Reputation: 1

You need to specify a terminal operation like .collect or .reduce.

Upvotes: 0

Related Questions