Using streams to collect into TreeSet with custom comparator
Working in Java 8, I have a TreeSet
defined like this:
private TreeSet<PositionReport> positionReports =
new TreeSet<>(Comparator.comparingLong(PositionReport::getTimestamp));
PositionReport
is a rather simple class defined like this:
public static final class PositionReport implements Cloneable {
private final long timestamp;
private final Position position;
public static PositionReport create(long timestamp, Position position) {
return new PositionReport(timestamp, position);
}
private PositionReport(long timestamp, Position position) {
this.timestamp = timestamp;
this.position = position;
}
public long getTimestamp() {
return timestamp;
}
public Position getPosition() {
return position;
}
}
This works fine.
Now I want to remove entries from the TreeSet positionReports
where timestamp
is older than some value. But I cannot figure out the correct Java 8 syntax to express this.
This attempt actually compiles, but gives me a new TreeSet
with an undefined comparator:
positionReports = positionReports
.stream()
.filter(p -> p.timestamp >= oldestKept)
.collect(Collectors.toCollection(TreeSet::new))
How do I express, that I want to collect into a TreeSet
with a comparator like Comparator.comparingLong(PositionReport::getTimestamp)
?
I would have thought something like
positionReports = positionReports
.stream()
.filter(p -> p.timestamp >= oldestKept)
.collect(
Collectors.toCollection(
TreeSet::TreeSet(Comparator.comparingLong(PositionReport::getTimestamp))
)
);
But this does not compile / appear to be valid syntax for method references.
Solution 1:
Method references can be used when you have a method (or constructor) that fits the shape of the target you're trying to satisfy. You can't use a method reference in this case because the shape you're targeting is a Supplier
, which takes no arguments, but what you have is a TreeSet
constructor, which does take an argument, and you need to specify what that argument is. So you have to take the less concise approach and use a lambda expression:
TreeSet<Report> toTreeSet(Collection<Report> reports, long timestamp) {
return reports.stream().filter(report -> report.timestamp() >= timestamp).collect(
Collectors.toCollection(
() -> new TreeSet<>(Comparator.comparingLong(Report::timestamp))
)
);
}
Solution 2:
This is easy just use next code:
positionReports = positionReports
.stream()
.filter(p -> p.timestamp >= oldestKept)
.collect(
Collectors.toCollection(()->new TreeSet<>(Comparator.comparingLong(PositionReport::getTimestamp)
)));
Solution 3:
You can just convert into a SortedSet at the end (provided that you don't mind the additional copy).
positionReports = positionReports
.stream()
.filter(p -> p.getTimeStamp() >= oldestKept)
.collect(Collectors.toSet());
return new TreeSet(positionReports);
Solution 4:
There is a method on Collection for this without having to use streams: default boolean removeIf(Predicate<? super E> filter)
. See Javadoc.
So your code could just look like this:
positionReports.removeIf(p -> p.timestamp < oldestKept);