简体   繁体   English

使用流通过自定义比较器收集到 TreeSet 中

[英]Using streams to collect into TreeSet with custom comparator

Working in Java 8, I have a TreeSet defined like this:在 Java 8 中工作,我有一个TreeSet定义如下:

private TreeSet<PositionReport> positionReports = 
        new TreeSet<>(Comparator.comparingLong(PositionReport::getTimestamp));

PositionReport is a rather simple class defined like this: 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;
    }
}

This works fine.这工作正常。

Now I want to remove entries from the TreeSet positionReports where timestamp is older than some value.现在我想从TreeSet positionReports中删除timestamp比某个值旧的条目。 But I cannot figure out the correct Java 8 syntax to express this.但我无法找出正确的 Java 8 语法来表达这一点。

This attempt actually compiles, but gives me a new TreeSet with an undefined comparator:这种尝试实际上可以编译,但给了我一个带有未定义比较器的新TreeSet

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) ?我如何表达,我想用Comparator.comparingLong(PositionReport::getTimestamp)这样的Comparator.comparingLong(PositionReport::getTimestamp)器收集到TreeSet

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.但这不能编译/似乎是方法引用的有效语法。

Method references are for when you have a method (or constructor) that already 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 and returns a collection, but what you have is a TreeSet constructor that does take an argument, and you need to specify what that argument is.在这种情况下,您不能使用方法引用,因为您所针对的形状是一个Supplier ,它不带参数并返回一个集合,但您拥有的是一个带参数的TreeSet构造函数,您需要指定什么这个论点是。 So you have to take the less concise approach and use a lambda expression:因此,您必须采用不那么简洁的方法并使用 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))
        )
    );
}

This is easy just use next code:这很容易,只需使用下一个代码:

    positionReports = positionReports
        .stream()
        .filter(p -> p.timestamp >= oldestKept)
        .collect(
            Collectors.toCollection(()->new TreeSet<>(Comparator.comparingLong(PositionReport::getTimestamp)
)));

You can just convert into a SortedSet at the end (provided that you don't mind the additional copy).您可以在最后转换为 SortedSet (前提是您不介意额外的副本)。

positionReports = positionReports
                .stream()
                .filter(p -> p.getTimeStamp() >= oldestKept)
                .collect(Collectors.toSet());

return new TreeSet(positionReports);

There is a method on Collection for this without having to use streams: default boolean removeIf(Predicate<? super E> filter) .有一种无需使用流的 Collection 方法: default boolean removeIf(Predicate<? super E> filter) See Javadoc .请参阅Javadoc

So your code could just look like this:所以你的代码可能看起来像这样:

positionReports.removeIf(p -> p.timestamp < oldestKept);

The problem with TreeSet is that the comparator that we want for sorting the items is used also for detecting duplicates when inserting items into the set. TreeSet 的问题在于,我们想要对项目进行排序的比较器也用于在将项目插入集合时检测重复项。 So if the comparator function is 0 for two items it wrongly discards one considering it as duplicate.因此,如果两个项目的比较器函数为 0,它会错误地丢弃一个,将其视为重复项。

The duplicates detection should be done by a separate correct hashCode method of the items.重复项检测应通过项目的单独正确 hashCode 方法完成。 I prefer to use a simple HashSet to prevent duplicates with a hashCode considering all properties (id and name in the example) and return a simple sorted List when getting the items (sorting only by name in the example):考虑到所有属性(示例中的 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM