2010-04-05 15 views
63

Tengo un objeto que define un 'orden de clasificación natural' usando Comparable <>. Estos se almacenan en TreeSets.manteniendo TreeSet ordenado como el objeto cambia el valor

Además de eliminar y volver a agregar el objeto, ¿hay alguna otra forma de actualizar el orden cuando se actualizan los miembros que se utilizan para definir el orden de clasificación?

+6

+1 bonita pregunta – skaffman

+0

¿Es solo curiosidad mórbida, o está buscando un beneficio específico, por ejemplo, en rendimiento o simplicidad de código? – greim

+2

Estaba buscando una manera simple no invasiva de mantener el orden de una colección a medida que los eventos actualizan los objetos dentro de ella. Alterar la membresía tiene efectos secundarios en otros consumidores de la colección. IMO, activar un recurso de un elemento parece una funcionalidad básica para una colección ordenada. – Stevko

Respuesta

11

Como han notado otros, no hay una forma integrada. Pero siempre se puede subclase que TreeSet, con su constructor (s) de elección, y añadir en la funcionalidad requerida:

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

A partir de entonces, tendrá que llamar ((UpdateableTreeSet)mySet).update(anElement, aValue) para actualizar el valor de la clasificación y la clasificación en sí . Esto requiere que implemente un método adicional update() en su objeto de datos.

+4

Esto ni siquiera funciona. remove (e) no podrá encontrar el elemento si su valor ya ha cambiado. –

+1

Por Dios, tienes razón. Editando para arreglar ... – tucuxi

+0

Para votar abajo, ¿por qué? – tucuxi

-1

No creo que exista una manera de hacerlo.

Puede usar un patrón de observador que notifica al conjunto de árboles cada vez que cambia un valor dentro de un elemento, luego lo elimina y lo vuelve a insertar.

De esta forma, puede mantener implícitamente la lista ordenada sin preocuparse de hacerlo a mano ... por supuesto, este enfoque deberá ampliar TreeSet modificando el comportamiento de inserción (estableciendo las mecánicas observadas/notificadas en el elemento recién agregado)

+3

No creo que eso funcione. Si el valor del elemento cambia de una manera que afecta el orden de clasificación, es bastante probable que * no pueda encontrarlo en el árbol * o eliminarlo. –

0

Solo integrado en forma es eliminar y volver a agregar.

2

Si realmente necesita usar un Set, entonces no tiene suerte, creo.

voy a lanzar en un comodín, sin embargo - si su situación es lo suficientemente flexible como para trabajar con un List en lugar de un Set, entonces se puede utilizar Collections.sort() para reordenar la List bajo demanda. Esto debería ser un rendimiento, si el orden List no tiene que cambiar mucho.

+1

No es una gran ventaja hacer eso: establecer garantiza búsquedas rápidas, inserción y eliminación, mientras que una lista requeriría usar Collections.binarySearch primero para ubicar el elemento.Es mejor seguir con la extracción y la reinserción. – tucuxi

+0

Me doy cuenta de eso, por eso dije "si su situación es lo suficientemente flexible". Los requisitos de rendimiento * pueden * ser lo suficientemente flojos como para permitir que una "Lista" sea práctica. – skaffman

+1

@tucuxi búsqueda binaria en una lista es O (log n). ¿Buscar en un TreeSet? También O (log n). –

1

Ayuda saber si sus objetos cambiarán en pequeños incrementos o en grandes cantidades. Si cada cambio es muy pequeño, haría muy bien en poner sus datos en una lista que mantenga ordenada. Para ello, usted tiene que

  1. busquedaBinaria para encontrar el índice del elemento
  2. modificar el elemento
  3. mientras que el elemento es mayor que su vecina derecha, intercambiarlo con su vecina derecha
  4. o si eso no sucedió: mientras que el elemento es menor que su vecino de la izquierda, cambiarlo por su vecino de la izquierda.

Pero tienes que asegurarte de que nadie puede cambiar el elemento sin pasar por "usted" para hacerlo.

EDIT: ¡También! Listas acristalamiento tiene una cierta ayuda para esto:

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

4

tuve un problema similar, encontrado este hilo y la respuesta de tucuxi (gracias!) basado en el cual implementé mi propio UpdateableTreeSet. Mi versión proporciona medios para

  • iterar sobre un conjunto tal,
  • horario actualizaciones elemento (diferido)/traslado de dentro del bucle
  • sin tener que crear una copia temporal del conjunto y finalmente
  • hacer todas las actualizaciones/eliminaciones como una operación masiva después de que el ciclo haya terminado.

UpdateableTreeSet oculta mucha de la complejidad del usuario. Además de las actualizaciones/eliminaciones masivas diferidas, la actualización/eliminación de un solo elemento como se muestra en tucuxi aún permanece disponible en la clase.

Actualización 2012-08-07: La clase está disponible en un pequeño GitHub repository que incluye un README introductorio con código de ejemplo esquemático, así como pruebas unitarias que muestran cómo (no) usarlo con más detalle.

+0

Me gusta la idea: simplemente implica colocar la matriz de "actualización diferida" dentro de UpdateableTreeSet, y luego llamar a "do-deferred-updates" para eliminar primero todas las actualizaciones y luego volverlas a insertar. – tucuxi

+0

Como mi ejemplo de código implica, esto es lo que he hecho. No es solo una idea, es una implementación real. ¿Ha seguido el enlace al código fuente y también ha comprobado otros archivos en el proyecto para ver cómo se usa la clase en casos más complicados? – kriegaex

+0

No es necesario, la idea es lo suficientemente clara, y aunque su explicación es demasiado larga, la voté como útil. – tucuxi

1

Busqué este problema cuando estaba tratando de implementar un panel de desplazamiento cinético similar a los rollos de rueda de iPhone de Apple. Los elementos de la TreeSet esta clase son:

/** 
* 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()); 
    } 
} 

El DoubleExpression puede tomar un momento para ser atado en una tarea runLater de la plataforma JavaFX, es por esto que el índice se incluye en esta clase de contenedor.

Como el scrollOffset siempre está cambiando según la posición de desplazamiento del usuario en la rueda de desplazamiento, necesitamos una forma de actualizar. Por lo general, el orden es siempre el mismo, ya que el desplazamiento es relativo a la posición del índice del artículo. El índice nunca cambia, pero el desplazamiento puede ser negativo o positivo dependiendo de la distancia relativa de los elementos de la propiedad vValue o hValue actual del ScrollPane.

Para actualizar bajo demanda solo cuando sea necesario, simplemente siga las instrucciones de la respuesta anterior de Tucuxi.

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

donde verticalOffsets es una TreeSet<ItemOffset>. Si imprime el conjunto cada vez que se invoca este fragmento de actualización, verá que está actualizado.

Cuestiones relacionadas