Reputation: 1430
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?
Upvotes: 5
Views: 2823
Reputation: 82929
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)]
Upvotes: 2
Reputation: 7279
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.
Upvotes: 3
Reputation: 40078
You can do it in the Predicate anyMatch
List<obj2> newCarList= obj2Arr.stream()
.filter(anObjOf2 -> obj1Arr
.stream()
.anyMatch(carReg -> {
if(carReg.getCarId().equals(anObjOf2.getCarId()))) {
carReg.setId(anObjOf2.getId());
return true;
}
return false;
}).collect(Collectors.toList());
Second Approach
Filtering first
List<obj2> newCarList= obj2Arr.stream()
.filter(anObjOf2 -> obj1Arr
.stream()
.anyMatch(carReg -> carReg.getCarId().equals(anObjOf2.getCarId())))
.collect(Collectors.toList());
Now Set the id
newCarList.forEach(obj->{
obj1Arr.forEach(o-> {
if(o.getCarId().equals(obj.getCarId()) {
o.setId(obj.getId());
}
};
});
Upvotes: 1
Reputation: 31978
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.
Upvotes: 1