Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

filter and set() in one stream

So I've some sample code I'm playing with to try and figure out this logic below. It's just one large main method and two POJOs, but it'll run. I'm debugging to get values at the point of termination.

public class Main {

    public static void main(String[] args) {

        obj1 obj1 = new obj1();
        obj1 obj12 = new obj1();

        obj2 obj2 = new obj2();
        obj2 obj22 = new obj2();

        obj1.id = 123L;
        obj1.carId = 1234L;
        obj12.id = 123L;
        obj12.carId = 1234L;

        obj2.carId = 1234L;
        obj22.carId = 12345L;

        ArrayList<obj1> obj1Arr = new ArrayList<>();
        ArrayList<obj2> obj2Arr = new ArrayList<>();

        obj1Arr.add(obj1);
        obj1Arr.add(obj12);

        obj2Arr.add(obj2);
        obj2Arr.add(obj22);

        List<obj2> newCarList= obj2Arr.stream()
                .filter(anObjOf2 -> obj1Arr
                        .stream()
                        .anyMatch(carReg -> carReg.getCarId().equals(anObjOf2.getCarId())))
                .collect(Collectors.toList());
    }
}

POJO1:

public class obj1 {

    Long id = null;
    Long carId = null;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public Long getCarId() {
        return carId;
    }

    public void setCarId(Long carId) {
        this.carId = carId;
    }
}

POJO2:

public class obj2 {

    Long id = null;
    Long carId = null;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public Long getCarId() {
        return carId;
    }

    public void setCarId(Long carId) {
        this.carId = carId;
    }
}

As you can see, I'm streaming and filtering out any objs that don't match on carId. My next goal is to then set id (not carId) on the matched object.

Basically, if obj1.carId == obj2.carId THEN set obj1.id = obj2.id.

My problem is I don't know how to do this in the same stream. Is it possible? I can't help but think there is a need for an iterator at this point?

like image 348
notAChance Avatar asked Feb 06 '19 13:02

notAChance


People also ask

Can we have multiple filter in stream?

Combining two filter instances creates more objects and hence more delegating code but this can change if you use method references rather than lambda expressions, e.g. replace filter(x -> x. isCool()) by filter(ItemType::isCool) .

What is the purpose of filter () method in streams?

The filter() function of the Java stream allows you to narrow down the stream's items based on a criterion. If you only want items that are even on your list, you can use the filter method to do this. This method accepts a predicate as an input and returns a list of elements that are the results of that predicate.

Can we have multiple filter in stream Java?

1. Overview. In this tutorial, We'll learn how to utilise stream filter() with several filter conditions (can be more than one condition). Normally, we apply a single condition to streams using filter() method with lambda and then store the results in Lists or Sets.


Video Answer


3 Answers

Don't do these two operations in one stream. It's a really bad style.

Collect the data first. Then mutate the elements. Something like:

Map<obj2, Optional<obj1>> map = 
    obj2Arr.stream()
           .collect(toMap(identity(), o2 -> obj1Arr.stream()
                                                  .filter(o1 -> o1.getCarId().equals(o2.getCarId()))
                                                  .findAny()));

map.forEach((o1, optO2) -> optO2.ifPresent(o2 -> o1.setId(o2.id)));

Otherwise you can just use nested for-each loops.

like image 140
ETO Avatar answered Oct 16 '22 09:10

ETO


I'd suggest first creating a Map mapping carId to id for each element in the first list (I've modified your class a bit to make it more readable and removed duplicate IDs, which I assume were a mistake):

@Data @AllArgsConstructor
class Car {
    Long id = null;
    Long carId = null;
}
List<Car> obj1Arr = Arrays.asList(new Car(1L, 123L), new Car(2L, 1234L));
List<Car> obj2Arr = Arrays.asList(new Car(0L, 1234L), new Car(0L, 12345L));

Map<Long, Long> carIdToId = obj1Arr.stream()
        .collect(Collectors.toMap(Car::getCarId, Car::getId));
// [Car(id=2, carId=1234), Car(id=0, carId=12345)]

Then you can use this Map to filter for cars with the same carID without having to compare with each other car. Then, use forEach just set the Id in the original list.

obj2Arr.stream()
        .filter(anObjOf2 -> carIdToId.containsKey(anObjOf2.getCarId()))
        .forEach(anObjOf2 -> anObjOf2.setId(carIdToId.get(anObjOf2.getCarId())));
System.out.println(obj2Arr);
// [Car(id=2, carId=1234), Car(id=0, carId=12345)]

If you want only the items with equal carId, you can set the ID in peek and then collect. This will still modify the original instances, though:

obj2Arr = obj2Arr.stream()
        .filter(anObjOf2 -> carIdToId.containsKey(anObjOf2.getCarId()))
        .peek(anObjOf2 -> anObjOf2.setId(carIdToId.get(anObjOf2.getCarId())))
        .collect(Collectors.toList());
System.out.println(obj2Arr);
// [Car(id=2, carId=1234)]

Or create a list of entirely new instances:

obj2Arr = obj2Arr.stream()
        .map(Car::getCarId)
        .filter(carIdToId::containsKey)
        .map(carId -> new Car(carIdToId.get(carId), carId))
        .collect(Collectors.toList());
System.out.println(obj2Arr);
// [Car(id=2, carId=1234)]
like image 43
tobias_k Avatar answered Oct 16 '22 09:10

tobias_k


If an O(m*n) solution works for you, then the simpler implementation would be using the for loop as:

// classes renamed for naming convention and relatiing to question at the same time 
List<ObjectTwo> newCarList = new ArrayList<>();
for(ObjectTwo objectTwo: objectTwoArr) {
    for (ObjectOne objectOne : objectOneArr) {
        if (objectTwo.getCarId().equals(objectOne.getCarId())) {
            objectOne.setId(objectTwo.getId());
            newCarList.add(objectTwo);
        }
    }
}

m and n being the respective sizes of the lists.

like image 38
Naman Avatar answered Oct 16 '22 10:10

Naman