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 obj
s 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?
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) .
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.
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.
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.
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)]
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With