2010-04-05 14 views
63

Ho un oggetto che definisce un "ordinamento naturale" utilizzando Comparabile <>. Questi vengono memorizzati in TreeSets.mantenimento ordinamento TreeSet come oggetto cambia valore

Oltre a rimuovere e riaggiungere l'oggetto, esiste un altro modo per aggiornare l'ordinamento quando vengono aggiornati i membri utilizzati per definire l'ordinamento?

+6

+1 bella domanda – skaffman

+0

Questa è solo curiosità morbosa, o stai cercando un beneficio specifico, diciamo in termini di prestazioni o semplicità del codice? – greim

+2

Stavo cercando un modo semplice e non invasivo per mantenere l'ordinamento di una raccolta mentre gli eventi aggiornano gli oggetti al suo interno. Alterare l'appartenenza ha effetti collaterali su altri consumatori della collezione. IMO, l'attivazione di un resort di un elemento sembra una funzionalità di base per una raccolta ordinata. Le interfacce grafiche – Stevko

risposta

11

Come altri hanno notato, non esiste un modo integrato. Ma si può sempre sottoclasse che TreeSet, con il costruttore (s) di scelta, e aggiungere nella funzionalità desiderata:

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

Da allora in poi, si dovrà chiamare ((UpdateableTreeSet)mySet).update(anElement, aValue) per aggiornare il valore di ordinamento e la stessa di smistamento . Ciò richiede l'implementazione di un ulteriore metodo update() nell'oggetto dati.

+4

Questo non funzionerà nemmeno. remove (e) non sarà in grado di trovare l'elemento se il suo valore è già cambiato. –

+1

Di golly, hai ragione. Modifica per correggere ... – tucuxi

+0

Per l'elettore - perché? – tucuxi

-1

Non penso che esista un modo semplice per farlo.

è possibile utilizzare un modello di osservatore che notifica l'TreeSet ogni volta che si cambia un valore all'interno di un elemento, quindi rimuove e re-inserisce.

In questo modo è possibile mantenere implicitamente la lista ordinata senza preoccuparsi di farlo a mano .. ovviamente questo approccio dovrà estendere TreeSet modificando il comportamento di inserimento (impostazione della meccanica osservata/notifica sull'oggetto appena aggiunto)

+3

Non penso che funzionerà. Se il valore dell'elemento cambia in un modo che influisce sull'ordinamento, è molto probabile che * non sia in grado di trovarlo nell'albero * o rimuoverlo. –

0

Solo in modo integrato è necessario rimuovere e aggiungere nuovamente.

2

Se hai davvero bisogno di usare un Set, allora sei sfortunato, penso.

ho intenzione di gettare in un jolly, anche se - se la vostra situazione è sufficientemente flessibile per lavorare con un List invece di un Set, quindi è possibile utilizzare Collections.sort() per riordinare il List su richiesta. Questo dovrebbe essere performante, se l'ordine List non deve essere modificato molto.

+1

Non c'è un grande vantaggio nel farlo - Set garantisce una ricerca, un inserimento e una rimozione rapidi, mentre un elenco richiede l'utilizzo di Collections.binarySearch per individuare l'elemento.È meglio attenersi alla rimozione e al reinserimento. – tucuxi

+0

Lo capisco, ed è per questo che ho detto "se la tua situazione è abbastanza flessibile". I requisiti di rendimento * potrebbero * essere abbastanza lenti da permettere a un 'List' di essere pratico. – skaffman

+1

@tucuxi la ricerca binaria in una lista è O (log n). Cerca in un set di alberi? Anche O (log n). –

1

Aiuta a sapere se gli oggetti cambieranno di piccoli incrementi o di grandi dimensioni. Se ogni modifica è molto piccola, faresti molto bene a mettere i tuoi dati in una lista che mantieni ordinati. Per fare questo, è necessario

  1. binarySearch per trovare l'indice dell'elemento
  2. modificare l'elemento
  3. mentre l'elemento è maggiore rispetto al suo vicino di destra, scambia con il suo vicino di destra
  4. o se ciò non accade: mentre l'elemento è inferiore al suo vicino di sinistra, scambialo con il suo vicino di sinistra.

Ma devi assicurarti che nessuno possa cambiare l'elemento senza passare attraverso "tu" per farlo.

MODIFICA: Inoltre! Liste vetri ha qualche supporto proprio per questo:

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

4

Ho avuto un problema simile, pensa che questa discussione e la risposta di TUCUXI (grazie!) in base al quale ho implementato il mio UpdateableTreeSet. La mia versione fornisce i mezzi per

  • iterazioni su una tale insieme,
  • programma di aggiornamenti elemento (differita)/assorbimenti dovuti all'interno del ciclo
  • senza dover creare una copia temporanea del set e, infine,
  • eseguire tutti gli aggiornamenti/rimozioni come operazione di massa dopo la fine del ciclo.

UpdateableTreeSet nasconde un sacco di complessità da parte dell'utente. Oltre agli aggiornamenti/rimozioni differite, l'aggiornamento/rimozione di un singolo elemento, come mostrato da tucuxi, rimane ancora disponibile nella classe.

Aggiornamento 2012-08-07: La classe è disponibile in un numero GitHub repository compreso un README introduttivo con codice di esempio schematico e test di unità che mostrano come (non) usarlo in modo più dettagliato.

+0

Mi piace l'idea: implicherebbe semplicemente posizionare l'array "deferred-update" all'interno di UpdateableTreeSet e quindi chiamare "do-post-updates" per rimuovere prima tutti gli aggiornamenti e quindi reinserirli. – tucuxi

+0

Come suggerisce il mio codice, questo è quello che ho fatto. Non è solo un'idea, è una vera implementazione. Hai seguito il link al codice sorgente e verificato anche altri file nel progetto per vedere come la classe viene utilizzata in casi più complicati? – kriegaex

+0

Non ce n'è bisogno - l'idea è abbastanza chiara, e anche se la tua spiegazione è un po 'troppo lunga, l'ho votata come utile. – tucuxi

1

Ho cercato questo problema quando stavo cercando di implementare un riquadro di scorrimento cinetico simile al rotellina della mela. Le voci nel TreeSet sono questa 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()); 
    } 
} 

Il DoubleExpression può prendere un momento di essere vincolato in un compito runLater della piattaforma JavaFX, questo è il motivo per cui l'indice è incluso in questa classe wrapper.

Poiché lo scrollOffset cambia in base alla posizione di scorrimento dell'utente sulla rotella di scorrimento, è necessario un aggiornamento. Solitamente l'ordine è sempre lo stesso, poiché l'offset è relativo alla posizione dell'indice dell'oggetto. L'indice non cambia mai, ma l'offset può essere negativo o positivo a seconda della distanza relativa degli articoli dalla proprietà vValue o hValue corrente di ScrollPane.

Per aggiornare su richiesta solo quando necessario, è sufficiente seguire la guida della risposta sopra riportata di Tucuxi.

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

dove verticalOffsets è un TreeSet<ItemOffset>. Se esegui una stampa del set ogni volta che viene chiamato questo snippet di aggiornamento, vedrai che è stato aggiornato.