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

时间:2014-02-11 09:06:39

标签: java collections java-8

在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 positionReportstimestamp早于某个值的条目。但我无法弄清楚正确的Java 8语法来表达这一点。

这个尝试实际上是编译的,但是给了我一个带有未定义比较器的新TreeSet

positionReports = positionReports
        .stream()
        .filter(p -> p.timestamp >= oldestKept)
        .collect(Collectors.toCollection(TreeSet::new))

我如何表达,我希望使用TreeSet这样的比较器收集到Comparator.comparingLong(PositionReport::getTimestamp)

我会想到像

这样的东西
positionReports = positionReports
        .stream()
        .filter(p -> p.timestamp >= oldestKept)
        .collect(
            Collectors.toCollection(
                TreeSet::TreeSet(Comparator.comparingLong(PositionReport::getTimestamp))
            )
        );

但是这不会编译/看起来是方法引用的有效语法。

6 个答案:

答案 0 :(得分:96)

Comparator<PositionReport> byTimestamp =
    Comparator.comparingLong(PositionReport::getTimestamp);

Supplier<TreeSet<PositionReport>> supplier =
    () -> new TreeSet<PositionReport>(byTimestamp);

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

答案 1 :(得分:12)

这很容易使用下一个代码:

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

答案 2 :(得分:7)

您最后可以转换为SortedSet(假设您不介意其他副本)。

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

return new TreeSet(positionReports);

答案 3 :(得分:6)

Collection上有一个方法,无需使用流:default boolean removeIf(Predicate<? super E> filter)。请参阅Javadoc

所以你的代码看起来像这样:

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

答案 4 :(得分:0)

TreeSet的问题在于,在将项目插入集合中时,我们想要对项目进行排序的比较器还用于检测重复项。因此,如果比较器函数的两项为0,则会错误地丢弃一项,而将其视为重复项。

重复项检测应通过单独的正确的hashCode方法进行。我更喜欢使用一个简单的HashSet来防止使用带有所有属性(示例中的id和name)的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());
    }
}

答案 5 :(得分:0)

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