[英]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.