2010-01-15 15 views
12

relacionadas: Does java have a "LinkedConcurrentHashMap" data structure?clase de colección Adecuado para los detectores de eventos en Java


Busco a una clase de colección para mantener las referencias a los detectores de eventos.

Idealmente me gustaría la colección a tener las siguientes propiedades (en orden de prioridad):

  1. mantiene el orden de inserción. Los oyentes anteriores pueden cancelar el evento, evitando que se entregue a los oyentes añadidos más tarde. Esto se romperá si se usa una clase como HashSet cuyo iterador puede devolver elementos en el orden incorrecto.
  2. Usa WeakReference s para que la lista de escucha no impida que los oyentes se recojan basura.
  3. La colección es Set, por lo que los duplicados se eliminan automáticamente.
  4. Iterator es una instantánea segura de subprocesos de la colección, no se ve afectada por la adición de nuevos oyentes. También permite que los eventos se entreguen en múltiples hilos. (Esto no es esencial; podría iterar sobre un clon del conjunto).

Conozco algunas clases que satisfacen algunos de estos criterios, pero no todos. Ejemplos:

  • java.util.LinkedHashSet (# 1 y # 3)
  • java.util.WeakHashMap, envuelto por Collections.newSetFromMap (# 2 y # 3)
  • javax.swing.event.EventListenerList (necesita un poco de sincronización extra) (# 1 y # 4)
  • java.util.concurrent.CopyOnWriteArraySet (# 1, # 3 y # 4)

Pero nada con ambos # 1 y # 2. ¿Existe una clase como esta en alguna biblioteca?

+0

¿Es realmente típico para los objetos del oyente implementar 'iguales'? –

+4

Además, ¿de quién es el trabajo de retener una referencia fuerte para cada oyente? Si escribiera un oyente para registrar varios eventos y registrarlo, ¡me sorprendería que desapareciera repentinamente unos minutos más tarde! –

+0

No el problema con equals es: '{Object o = new Object(); WeakReference r1 = new WeakReference (o), r2 = new WeakReference (o); return r1.equals (r2);} 'returns' false' – finnw

Respuesta

6

Puede usar WeakListeners (consulte http://bits.netbeans.org/dev/javadoc/org-openide-util/org/openide/util/WeakListeners.html) y CopyOnWriteArraySet.

  1. Implemente un método remove(ListenerType listener) en la fuente de su evento.
  2. En el método de register(SomeListener listener), agregue un WeakListener a la colección en su lugar:

    listenerCollection.put((ListenerType)WeakListeners.create ( ListenerType.class, listener, this));

Cuando el oyente real, se elimina de la memoria, se le notificará al oyente débil, y va a anular su registro . (Es por eso que necesita la referencia a la fuente (this) para el registro). La anulación del registro se realiza mediante la reflexión llamando al método eliminar de la fuente.

+0

Sí, este es el tipo de cosas que tenía en mente. +1 – finnw

0

Usted puede envolver cada referencia de escucha en un WeakReferencey luego usar CopyOnWriteArraySet.

+0

Dos problemas con eso: 1. las referencias recopiladas por basura permanecerán en el set forever (WeakHashMap los borra automáticamente) y 2. 'WeakReference' no anula' equals() ', por lo que puede terminar con múltiples referencias al mismo oyente en el conjunto pero no puede decir que son duplicados. – finnw

+0

Es cierto. Sin embargo, quizás podría superar esto ampliando CopyOnWriteArraySet para examinar el referente en lugar de WeakReference cuando la colección muta y elimina manualmente las referencias obsoletas. No creo en nada en el estándar. Collections ofrece todo lo que desea desde el primer momento. –

0

Puede extender WeakReference para anular equals y hashcode, luego puede usarlos en un LinkedHashSet.

7

Voy a comenzar diciendo que usted tiene un par de requisitos que no tienen sentido juntos. Está buscando una colección que elimine duplicados y admita referencias débiles, lo que me indica que los oyentes pueden aparecer y desaparecer en momentos indeterminados. Sin embargo, desea mantener el orden de inserción y permitir que un oyente cancele todas las notificaciones posteriores . Para mí, esto suena como una receta para errores difíciles de encontrar, y recomiendo encarecidamente repensarlo.

Dicho esto, tiene un requisito que impulsa la solución: no desea el ConcurrentModificationException que podría provenir de un iterador normal. Lo que significa que tendrá que copiar la lista original. En el camino, se puede comprobar y eliminar las referencias vacías:

// the master list 
List<WeakReference<MyListener>> _list = new ArrayList<WeakReference<MyListener>>(); 

// inside your send-notification method 
List<MyListener> toNotify = new ArrayList<MyListener>(_list.size()); 
Iterator<WeakReference<MyListener>> itx = _list.iterator(); 
while (itx.hasNext()) 
{ 
    WeakReference<MyListener> ref = itx.next(); 
    MyListener lsnr = ref.get(); 
    if (lsnr != null) 
     toNotify.add(lsnr); 
    else 
     itx.remove(); 
} 

// now iterate "toNotify" and invoke the listeners 

Probablemente se esté volviendo loco ahora, diciendo "una lista que tenga una estructura de datos lineal!No puedo usar eso, ¡la inserción es O (N)! "

Bueno, sí puedes. No sé cuántos oyentes tienes planeado tener, pero mientras seas < 100 (y es más probable < 100.000), el costo de una búsqueda lineal para insertar y retirar no va a importar.

Mucho más interesante desde el punto de vista de codificación es cómo hacer frente a la referencia débil. se habrá dado cuenta de que me desreferencia explícitamente, en una variable, antes de probar el referente para nulo. Este es un código críticamente importante cuando se trata de objetos de referencia: aunque es extremadamente improbable que el referente se recopile entre dos llamadas al get(), es posible.

Lo que me lleva al WeakReference en sí. Tendrá que crear su propia subclase que anule los métodos equals() y hashCode() para delegar en su referente. Pensé que tenía una clase así, pero aparentemente no, así que te dejaré implementarla.

1

Un conjunto es la colección adecuada para usar con los oyentes.

Si confía en el orden de inserción de los oyentes, su diseño está roto. Se pierde el sentido de que los oyentes están AISLADOS e INDEPENDIENTES de otros oyentes. Use Conjuntos en lugar de Listas.

Si confías en WeakReferences, tu diseño está quebrado. Elimine los oyentes en el mismo objeto donde lo agregó. Este SYMMETRY admite LECTURA y MANTENIMIENTO. Para resolver errores de programación de olvidados, las subrublicaciones de oyentes con referencias débiles solo ocultan el problema.

Si proporciona su colección de oyentes a otros objetos que no sean su objeto observado, entonces su diseño está roto. Mantenga el conjunto privado para admitir ENCAPSULATION.

Si reemplaza equals y hashcode de sus oyentes, su diseño se rompe. Oculta el problema de las llamadas a funciones innecesarias. Evite llamadas innecesarias en su lugar. Después de todos los equivalentes iguales y hashcode de oyentes no es necesario.

En entornos MULTITHREADING coloque un MONITOR en el recurso "listeners" mientras lo agrega, quita o itera sobre él. Puede crear una COPIA DEFENSIVA antes de iterar para evitar una Excepción de ConcurrentModificación. Entonces la iteración no tiene que ser SINCRONIZADA, pero la acción de copia debería.

Cualquier otro requisito debe adaptarse o reformularse para que coincida con estas afirmaciones.Cualquier otra práctica dará lugar a un código inmanejable, pérdidas de memoria debido a la falta de aislamiento, independencia, encapsulación y claridad.

Cuestiones relacionadas