web-dev-qa-db-fra.com

Utilisation de flux à rassembler dans TreeSet avec un comparateur personnalisé

Travailler dans Java 8, j’ai un TreeSet défini comme ceci:

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

PositionReport est une classe assez simple définie comme ceci:

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;
    }
}

Cela fonctionne bien.

Maintenant, je veux supprimer des entrées du TreeSet positionReportstimestamp est plus ancien qu'une valeur. Mais je ne peux pas comprendre la syntaxe correcte Java 8 pour exprimer cela.

Cette tentative compile, mais me donne un nouveau TreeSet avec un comparateur indéfini:

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

Comment puis-je exprimer ce que je veux rassembler dans un TreeSet avec un comparateur comme Comparator.comparingLong(PositionReport::getTimestamp)?

J'aurais pensé quelque chose comme

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

Mais cela ne compile pas/ne semble pas être une syntaxe valide pour les références de méthode.

77
tbsalling
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));
101
gdejohn

C'est simple, utilisez le code suivant:

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

Vous pouvez simplement convertir un SortedSet à la fin (à condition que la copie supplémentaire ne vous dérange pas).

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

return new TreeSet(positionReports);
8
Daniel Scott

Il existe une méthode sur Collection pour cela sans avoir à utiliser de flux: default boolean removeIf(Predicate<? super E> filter). Voir Javadoc .

Donc, votre code pourrait ressembler à ceci:

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

Le problème avec TreeSet est que le comparateur que nous voulons pour trier les éléments est également utilisé pour détecter les doublons lors de l'insertion d'éléments dans l'ensemble. Donc, si la fonction de comparaison est 0 pour deux éléments, elle en écarte à tort un en la considérant comme un doublon.

La détection des doublons doit être effectuée par une méthode distincte, hashCode, des éléments. Je préfère utiliser un simple HashSet pour éviter les doublons avec un hashCode prenant en compte toutes les propriétés (id et name dans l'exemple) et renvoyer une simple liste triée lors de l'extraction des éléments (tri uniquement par nom dans l'exemple):

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());
    }
}
0
Daniel Mora
positionReports = positionReports.stream()
                             .filter(p -> p.getTimeStamp() >= oldestKept)
                             .collect(Collectors.toCollection(() -> new 
TreeSet<PositionReport>(Comparator.comparingLong(PositionReport::getTimestamp))));
0
Cyril Sojan