Trabajando en Java 8, tengo una TreeSet
definición como esta:
private TreeSet<PositionReport> positionReports =
new TreeSet<>(Comparator.comparingLong(PositionReport::getTimestamp));
PositionReport
es una clase bastante simple definida así:
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;
}
}
Esto funciona bien.
Ahora quiero eliminar las entradas de TreeSet positionReports
donde timestamp
es más antiguo que algún valor. Pero no puedo encontrar la sintaxis correcta de Java 8 para expresar esto.
Este intento en realidad se compila, pero me da un nuevo TreeSet
con un comparador indefinido:
positionReports = positionReports
.stream()
.filter(p -> p.timestamp >= oldestKept)
.collect(Collectors.toCollection(TreeSet::new))
¿Cómo expreso que quiero recopilar en TreeSet
un comparador como Comparator.comparingLong(PositionReport::getTimestamp)
?
Hubiera pensado algo como
positionReports = positionReports
.stream()
.filter(p -> p.timestamp >= oldestKept)
.collect(
Collectors.toCollection(
TreeSet::TreeSet(Comparator.comparingLong(PositionReport::getTimestamp))
)
);
Pero esto no compila / parece ser una sintaxis válida para referencias a métodos.
fuente
.collect(Collectors.toCollection(TreeSet::new));
toCollection in class Collectors cannot be applied to given types
Collectors::toCollection
: aSupplier
que devuelve aCollection
.Supplier
es un tipo con un solo método abstracto, lo que significa que puede ser el objetivo de una expresión lambda como en esta respuesta. La expresión lambda no debe tomar argumentos (de ahí la lista de argumentos vacía()
) y devolver una colección con un tipo de elemento que coincida con el tipo de elementos en el flujo que está recopilando (en este caso, aTreeSet<PositionReport>
).Esto es fácil, solo use el siguiente código:
positionReports = positionReports .stream() .filter(p -> p.timestamp >= oldestKept) .collect( Collectors.toCollection(()->new TreeSet<>(Comparator.comparingLong(PositionReport::getTimestamp) )));
fuente
Puede convertirlo en un SortedSet al final (siempre que no le importe la copia adicional).
positionReports = positionReports .stream() .filter(p -> p.getTimeStamp() >= oldestKept) .collect(Collectors.toSet()); return new TreeSet(positionReports);
fuente
compareTo()
0 mientras que el otro podría no tenerlo para algunas comparaciones. Todos los quecompareTo()
son 0 está perdido ya que este es un conjunto.)Hay un método de recopilación para esto sin tener que utilizar corrientes:
default boolean removeIf(Predicate<? super E> filter)
. Ver Javadoc .Entonces su código podría verse así:
fuente
El problema con TreeSet es que el comparador que queremos para clasificar los elementos se usa también para detectar duplicados al insertar elementos en el conjunto. Entonces, si la función de comparación es 0 para dos elementos, descarta incorrectamente uno considerándolo duplicado.
La detección de duplicados debe realizarse mediante un método hashCode correcto separado de los elementos. Prefiero usar un HashSet simple para evitar duplicados con un hashCode considerando todas las propiedades (id y nombre en el ejemplo) y devolver una lista ordenada simple al obtener los elementos (ordenando solo por nombre en el ejemplo):
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()); } }
fuente
positionReports = positionReports.stream() .filter(p -> p.getTimeStamp() >= oldestKept) .collect(Collectors.toCollection(() -> new TreeSet<PositionReport>(Comparator.comparingLong(PositionReport::getTimestamp))));
fuente