[英]Is it better to use a TreeSet or ArrayList when using a custom comparator
[英]Using streams to collect into TreeSet with custom comparator
在 Java 8 中工作,我有一个TreeSet
定义如下:
private TreeSet<PositionReport> positionReports =
new TreeSet<>(Comparator.comparingLong(PositionReport::getTimestamp));
PositionReport
是一个相当简单的类,定义如下:
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;
}
}
这工作正常。
现在我想从TreeSet positionReports
中删除timestamp
比某个值旧的条目。 但我无法找出正确的 Java 8 语法来表达这一点。
这种尝试实际上可以编译,但给了我一个带有未定义比较器的新TreeSet
:
positionReports = positionReports
.stream()
.filter(p -> p.timestamp >= oldestKept)
.collect(Collectors.toCollection(TreeSet::new))
我如何表达,我想用Comparator.comparingLong(PositionReport::getTimestamp)
这样的Comparator.comparingLong(PositionReport::getTimestamp)
器收集到TreeSet
?
我会想像
positionReports = positionReports
.stream()
.filter(p -> p.timestamp >= oldestKept)
.collect(
Collectors.toCollection(
TreeSet::TreeSet(Comparator.comparingLong(PositionReport::getTimestamp))
)
);
但这不能编译/似乎是方法引用的有效语法。
方法引用适用于当您有一个方法(或构造函数)已经适合您要满足的目标的形状时。 在这种情况下,您不能使用方法引用,因为您所针对的形状是一个Supplier
,它不带参数并返回一个集合,但您拥有的是一个带参数的TreeSet
构造函数,您需要指定什么这个论点是。 因此,您必须采用不那么简洁的方法并使用 lambda 表达式:
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))
)
);
}
这很容易,只需使用下一个代码:
positionReports = positionReports
.stream()
.filter(p -> p.timestamp >= oldestKept)
.collect(
Collectors.toCollection(()->new TreeSet<>(Comparator.comparingLong(PositionReport::getTimestamp)
)));
您可以在最后转换为 SortedSet (前提是您不介意额外的副本)。
positionReports = positionReports
.stream()
.filter(p -> p.getTimeStamp() >= oldestKept)
.collect(Collectors.toSet());
return new TreeSet(positionReports);
有一种无需使用流的 Collection 方法: default boolean removeIf(Predicate<? super E> filter)
。 请参阅Javadoc 。
所以你的代码可能看起来像这样:
positionReports.removeIf(p -> p.timestamp < oldestKept);
TreeSet 的问题在于,我们想要对项目进行排序的比较器也用于在将项目插入集合时检测重复项。 因此,如果两个项目的比较器函数为 0,它会错误地丢弃一个,将其视为重复项。
重复项检测应通过项目的单独正确 hashCode 方法完成。 考虑到所有属性(示例中的 id 和名称),我更喜欢使用简单的 HashSet 来防止使用 hashCode 重复,并在获取项目时返回一个简单的排序列表(示例中仅按名称排序):
public class ProductAvailableFiltersDTO {
private Set<FilterItem> category_ids = new HashSet<>();
public List<FilterItem> getCategory_ids() {
return category_ids.stream()
.sorted(Comparator.comparing(FilterItem::getName))
.collect(Collectors.toList());
}
public void setCategory_ids(List<FilterItem> category_ids) {
this.category_ids.clear();
if (CollectionUtils.isNotEmpty(category_ids)) {
this.category_ids.addAll(category_ids);
}
}
}
public class FilterItem {
private String id;
private String name;
public FilterItem(String id, String name) {
this.id = id;
this.name = name;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof FilterItem)) return false;
FilterItem that = (FilterItem) o;
return Objects.equals(getId(), that.getId()) &&
Objects.equals(getName(), that.getName());
}
@Override
public int hashCode() {
return Objects.hash(getId(), getName());
}
}
positionReports = positionReports.stream()
.filter(p -> p.getTimeStamp() >= oldestKept)
.collect(Collectors.toCollection(() -> new
TreeSet<PositionReport>(Comparator.comparingLong(PositionReport::getTimestamp))));
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.