Java Streams for looping twice over same list

Multi tool use
Java Streams for looping twice over same list
I am new to java 8, and i have a problem understanding streams for some reason. Let's say we have a list of objects List< MyObject >, where MyObject has 2 fields : Long Id, Date insertTime, and i would like to remove elements with same ID and earlier time.
With 2 for loops it is something like this :
for(MyObject object : myObjects) {
for(MyObject tmpObject : myObjects) {
if(object.getId() == tmpObject.getId()) {
if(object.getInsertDate().after(tmpObject.getInsertDate()))
myObjects.remove(tmpObject);
else
myObjects.remove(object);
}
}
}
How would this look when using streams?
@Michael my bad. Than i would need another list or array to store the response data.
Let's say i have
So i need as a result
Thank you all.
On a side note, you should always use
{
and }
on your statements as it will make your life easier in the long run.– notyou
Jul 2 at 7:31
{
}
@AxelH I suppose the
equals
method is the wrong place for that. The equals method should return true for the same id (and optionally the same date)– Glains
Jul 2 at 7:39
equals
Yep @Glains, I didn't read that the date should be earlier ... so it is incorrect.
– AxelH
Jul 2 at 7:42
you can do somthing like below : List<Optional<MyObj>> collect = list.stream().collect(groupingBy(MyObj::getId, maxBy(Comparator.comparing(MyObj::getDate)))).values().stream().collect(toList());
– Laxmikant
Jul 2 at 8:13
2 Answers
2
group by id and then for each id pick the one with the largest insertion date. You can replace Sample by MyObject.
Map<Long, List<Sample>> map = list.stream().collect(Collectors.groupingBy(Sample::getId));
map.values().stream()
.map(samples -> Collections.max(
samples, Comparator.comparing(Sample::getInsertDate)))
.collect(Collectors.toList());
Instead of collecting each group into a
List
, to select the maximum element in a second step, you can let the groupingBy
operation collect the maximum element in the first place, like in this answer.– Holger
Jul 2 at 11:30
List
groupingBy
You can do it like so,
List<MyObject> latestObjects = myObjects.stream()
.collect(Collectors.collectingAndThen(
Collectors.groupingBy(MyObject::getId,
Collectors.maxBy(Comparator.comparing(MyObject::getInsertTime))),
map -> map.values().stream().map(op -> op.orElse(null)).collect(Collectors.toList())));
First group the objects by their Id value, then reduce it to keep only the latest object for each Id value, finally collect them into a List
.
List
Update
This can further be improved as per the comment below. Here's the augmented version of it.
List<MyObject> latestObjects = myObjects.stream()
.collect(Collectors.collectingAndThen(
Collectors.toMap(MyObject::getId, Function.identity(),
BinaryOperator.maxBy(Comparator.comparing(MyObject::getInsertTime))),
map -> new ArrayList<MyObject>(map.values())));
Whenever the downstream collector is a reduction (i.e. all these cases where you have to deal with an
Optional
afterwards), you may use toMap
instead of groupingBy
: List<MyObject> latestObjects = myObjects.stream() .collect(Collectors.collectingAndThen( Collectors.toMap(MyObject::getId, Function.identity(), BinaryOperator.maxBy(Comparator.comparing(MyObject::getInsertTime))), map -> new ArrayList<>(map.values())));
– Holger
Jul 2 at 11:36
Optional
toMap
groupingBy
List<MyObject> latestObjects = myObjects.stream() .collect(Collectors.collectingAndThen( Collectors.toMap(MyObject::getId, Function.identity(), BinaryOperator.maxBy(Comparator.comparing(MyObject::getInsertTime))), map -> new ArrayList<>(map.values())));
Totally agree with you. Thanks for the invaluable feedback !
– Ravindra Ranwala
Jul 2 at 14:37
By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.
"With 2 for loops it is something like this" I'm afraid it's not, because you can't remove from a list while you're iterating over it.
– Michael
Jul 2 at 7:24