Использование потоков для сбора в набор деревьев с помощью пользовательского компаратора
работая в 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))
как мне выразить, что я хочу собрать в TreeSet
С компаратором, такие как Comparator.comparingLong(PositionReport::getTimestamp)
?
Я бы подумал что-то вроде
positionReports = positionReports
.stream()
.filter(p -> p.timestamp >= oldestKept)
.collect(
Collectors.toCollection(
TreeSet::TreeSet(Comparator.comparingLong(PositionReport::getTimestamp))
)
);
но это не компилируется / кажется допустимым синтаксисом для ссылок на методы.
5 ответов:
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));
Это просто использовать следующий код:
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);
существует метод сбора для этого без использования потоков:
default boolean removeIf(Predicate<? super E> filter)
. Смотрите документация.Так что ваш код может выглядеть так:
positionReports.removeIf(p -> p.timestamp < oldestKept);
проблема с TreeSet заключается в том, что компаратор, который мы хотим для сортировки элементов, используется также для обнаружения дубликатов при вставке элементов в набор. Поэтому, если функция компаратора равна 0 для двух элементов, она ошибочно отбрасывает один, считая его дубликатом.
обнаружение дубликатов должно выполняться отдельным правильным методом хэш-кода элементов. Я предпочитаю использовать простой хэш-набор, чтобы предотвратить дубликаты с хэш-кодом, учитывая все свойства (id и имя в пример) и возвращает простой отсортированный список при получении элементов (сортировка только по имени в Примере):
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()); } }