web-dev-qa-db-fra.com

maintien du tri TreeSet lorsque l'objet change de valeur

J'ai un objet qui définit un "ordre de tri naturel" en utilisant Comparable <>. Ceux-ci sont stockés dans TreeSets.

Outre la suppression et l'ajout de l'objet, existe-t-il une autre façon de mettre à jour le tri lorsque les membres utilisés pour définir l'ordre de tri sont mis à jour?

69
Stevko

Comme d'autres l'ont fait remarquer, il n'existe aucun moyen intégré. Mais vous pouvez toujours sous-classer ce TreeSet, avec le ou les constructeurs de votre choix, et ajouter les fonctionnalités requises:

public class UpdateableTreeSet<T extends Updateable> extends TreeSet<T> {

    // definition of updateable
    interface Updateable{ void update(Object value); }

    // constructors here
    ...

    // 'update' method; returns false if removal fails or duplicate after update
    public boolean update(T e, Object value) {
       if (remove(e)) {
           e.update(value);
           return add(e);
       } else { 
           return false;
       }
    }
}

À partir de là, vous devrez appeler ((UpdateableTreeSet)mySet).update(anElement, aValue) Pour mettre à jour la valeur de tri et le tri lui-même. Cela vous oblige à implémenter une méthode update() supplémentaire dans votre objet de données.

14
tucuxi

J'ai eu un problème similaire, j'ai trouvé ce fil et la réponse de tucuxi (merci!) Sur la base desquels j'ai implémenté mon propre UpdateableTreeSet. Ma version permet de

  • itérer sur un tel ensemble,
  • planifier (différé) les mises à jour/suppressions d'éléments depuis la boucle
  • sans avoir à créer une copie temporaire de l'ensemble et enfin
  • effectuez toutes les mises à jour/suppressions en tant qu'opération en bloc une fois la boucle terminée.

UpdateableTreeSet cache une grande partie de la complexité à l'utilisateur. En plus des mises à jour/suppressions groupées différées, la mise à jour/suppression d'un seul élément, comme indiqué par tucuxi, reste disponible dans la classe.

Mise à jour 2012-08-07: La classe est disponible dans un petit référentiel GitHub y compris une introduction README avec un exemple de code schématique ainsi que des tests unitaires montrant comment (non) pour l'utiliser plus en détail.

5
kriegaex

Si vous avez vraiment besoin d'utiliser un Set, alors vous n'avez pas de chance, je pense.

Je vais ajouter un caractère générique, cependant - si votre situation est suffisamment flexible pour fonctionner avec un List au lieu d'un Set, alors vous pouvez utiliser Collections.sort() pour trier à nouveau le List à la demande. Cela devrait être performant, si l'ordre List n'a pas besoin d'être beaucoup changé.

3
skaffman

J'ai recherché ce problème lorsque j'essayais d'implémenter un volet de défilement cinétique similaire au Apple molette de l'iPhone défile. Les éléments dans le TreeSet sont de cette classe:

/**
 * Data object that contains a {@code DoubleExpression} bound to an item's
 * relative distance away from the current {@link ScrollPane#vvalueProperty()} or
 * {@link ScrollPane#hvalueProperty()}. Also contains the item index of the
 * scrollable content.
 */
private static final class ItemOffset implements Comparable<ItemOffset> {

    /**
     * Used for floor or ceiling searches into a navigable set. Used to find the
     * nearest {@code ItemOffset} to the current vValue or hValue of the scroll
     * pane using {@link NavigableSet#ceiling(Object)} or
     * {@link NavigableSet#floor(Object)}.
     */
    private static final ItemOffset ZERO = new ItemOffset(new SimpleDoubleProperty(0), -1);

    /**
     * The current offset of this item from the scroll vValue or hValue. This
     * offset is transformed into a real pixel length of the item distance from
     * the current scroll position.
     */
    private final DoubleExpression scrollOffset;

    /** The item index in the list of scrollable content. */
    private final int index;

    ItemOffset(DoubleExpression offset, int index) {
        this.scrollOffset = offset;
        this.index = index;
    }

    /** {@inheritDoc} */
    @Override
    public int compareTo(ItemOffset other) {
        double d1 = scrollOffset.get();
        double d2 = other.scrollOffset.get();

        if (d1 < d2) {
            return -1;
        }
        if (d1 > d2) {
            return 1;
        }

        // Double expression has yet to be bound
        // If we don't compare by index we will
        // have a lot of values ejected from the
        // navigable set since they will be equal.
        return Integer.compare(index, other.index);
    }

    /** {@inheritDoc} */
    @Override
    public String toString() {
        return index + "=" + String.format("%#.4f", scrollOffset.get());
    }
}

DoubleExpression peut prendre un moment pour être lié dans une tâche runLater de la plate-forme JavaFX, c'est pourquoi l'index est inclus dans cette classe wrapper.

Puisque le scrollOffset change toujours en fonction de la position de défilement de l'utilisateur sur la molette de défilement, nous avons besoin d'un moyen de mise à jour. Généralement, l'ordre est toujours le même, car le décalage est relatif à la position d'index de l'article. L'index ne change jamais, mais le décalage peut être négatif ou positif selon la distance relative des éléments par rapport à la propriété vValue ou hValue actuelle de ScrollPane.

Pour mettre à jour sur demande uniquement en cas de besoin, suivez simplement les conseils de la réponse ci-dessus de Tucuxi.

ItemOffset first = verticalOffsets.first();
verticalOffsets.remove(first);
verticalOffsets.add(first);

verticalOffsets est un TreeSet<ItemOffset>. Si vous effectuez une impression de l'ensemble à chaque appel de cet extrait de mise à jour, vous verrez qu'il est mis à jour.

1
ghostNet

Il permet de savoir si vos objets changeront par petits incréments ou par grands. Si chaque modification est très petite, vous feriez bien de mettre vos données dans une liste que vous gardez triée. Pour ce faire, vous devez

  1. binarySearch pour trouver l'index de l'élément
  2. modifier l'élément
  3. tandis que l'élément est supérieur à son voisin de droite, échangez-le avec son voisin de droite
  4. ou si cela ne s'est pas produit: alors que l'élément est inférieur à son voisin de gauche, échangez-le avec son voisin de gauche.

Mais vous devez vous assurer que personne ne peut changer l'élément sans passer par "vous" pour le faire.

EDIT: Aussi! Les listes glacées ont un certain support pour ceci:

http://publicobject.com/glazedlists/glazedlists-1.5.0/api/ca/odell/glazedlists/ObservableElementList.html

1

La seule manière intégrée est de supprimer et de rajouter.

0
Robby Pond