Vraag Geschikte verzamelklasse voor gebeurtenislisteners in Java


Verwant: Heeft Java een "LinkedConcurrentHashMap" -gegevensstructuur?


Ik ben op zoek naar een verzamelingsklasse die verwijzingen naar gebeurtenislisteners bevat.

Idealiter zou ik willen dat de verzameling de volgende eigenschappen heeft (in volgorde van prioriteit):

  1. Behoud invoegvolgorde. De eerdere luisteraars kunnen de gebeurtenis annuleren, waardoor wordt voorkomen dat deze wordt afgeleverd aan luisteraars die later worden toegevoegd. Dit zal breken als u een klasse zoals gebruikt HashSet wiens iterator elementen in de verkeerde volgorde kan retourneren.
  2. Toepassingen WeakReferencezodat de lijst met luisteraars niet belet dat de luisteraars worden verzameld.
  3. De verzameling is een Set, dus duplicaten worden automatisch verwijderd.
  4. De Iterator is een thread-safe momentopname van de collectie, onaangetast door de toevoeging van nieuwe luisteraars. Staat ook toe dat gebeurtenissen op meerdere threads worden afgeleverd. (Dit is niet essentieel - ik zou in plaats daarvan een kloon van de set kunnen herhalen.)

Ik ben me bewust van enkele klassen die aan enkele maar niet aan al deze criteria voldoen. Voorbeelden:

  • java.util.LinkedHashSet (# 1 en # 3)
  • java.util.WeakHashMap, ingepakt door Collections.newSetFromMap (# 2 en # 3)
  • javax.swing.event.EventListenerList (heeft wat extra synchronisatie nodig) (# 1 en # 4)
  • java.util.concurrent.CopyOnWriteArraySet (# 1, # 3 en # 4)

Maar niets met zowel # 1 als # 2. Bestaat zo'n klasse ergens in een bibliotheek?


12
2018-01-15 15:34


oorsprong


antwoorden:


U zou WeakListeners kunnen gebruiken (zie http://bits.netbeans.org/dev/javadoc/org-openide-util/org/openide/util/WeakListeners.html) en CopyOnWriteArraySet.

  1. Implementeer een remove(ListenerType listener) methode in uw evenementbron.
  2. In uw register(SomeListener listener) methode, voeg in plaats hiervan een WeakListener toe aan de verzameling:

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

Wanneer de echte luisteraar uit het geheugen wordt verwijderd, wordt de zwakke luisteraar op de hoogte gebracht en zal hij zichzelf afmelden. (Dit is waarom het de verwijzing naar de bron nodig heeft (this) voor de registratie.) De afmelding gebeurt met behulp van reflectie door de methode remove van de bron aan te roepen.


6
2018-01-18 15:34



Ik begin met te zeggen dat je een aantal vereisten hebt die samen niet kloppen. Je bent op zoek naar een verzameling die duplicaten verwijdert en zwakke verwijzingen ondersteunt, wat aangeeft dat luisteraars op onbepaalde tijden kunnen verschijnen en verdwijnen. Toch wilt u de invoegvolgorde handhaven en één luisteraar toestaan ​​om alles te annuleren volgend meldingen. Voor mij klinkt dit als een recept voor moeilijk te vinden bugs, en ik raad sterk aan het opnieuw te bekijken.

Dat gezegd hebbende, heeft u één vereiste die de oplossing vrijwel aandrijft: u wilt het niet ConcurrentModificationException dat zou kunnen komen van een normale iterator. Dat betekent dat je de originele lijst moet kopiëren. Onderweg kunt u de lege verwijzingen controleren en verwijderen:

// 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

Je bent waarschijnlijk nu helemaal in de war en zegt "een lijst! Dat is een lineaire gegevensstructuur! Ik kan dat niet gebruiken, invoegen is O (N)!"

Nou ja, dat kan. Ik weet niet hoeveel luisteraars je van plan bent te hebben. Maar zolang u <100 (en waarschijnlijker <100.000) bent, zullen de kosten van een lineaire zoekopdracht voor invoegen en verwijderen er niet toe doen.

Veel interessanter vanuit een coderingsperspectief is hoe je omgaat met de zwakke referentie. Merk op dat ik het expliciet verwijs naar een variabele, voordat ik de referent voor nul test. Dit is een uiterst belangrijke code bij het behandelen van referentieobjecten: hoewel het uiterst onwaarschijnlijk is dat de referent tussen twee oproepen wordt verzameld get(), het is mogelijk.

Dat brengt me bij de WeakReference zelf. U moet uw eigen subklasse maken die de waarde overschrijft equals() en hashCode() methoden om te delegeren naar zijn referent. Ik dacht dat ik zo'n klasse gewoon rondslingerde, maar blijkbaar niet, dus laat het aan jou over om het te implementeren.


7
2018-01-18 13:37



Een set is de juiste verzameling om te gebruiken met luisteraars.

Als je vertrouwt op de invoegvolgorde van luisteraars, is je ontwerp kapot. Het mist het punt dat luisteraars worden GEÏSOLEERD en ONAFHANKELIJK van andere luisteraars. Gebruik Sets in plaats van Lijsten.

Als u op WeakReferences vertrouwt, is uw ontwerp kapot. Verwijder luisteraars in hetzelfde object als waar u het hebt toegevoegd. Deze SYMMETRIE ondersteunt LEESBAARHEID en handhaafbaarheid. Programmafouten van forgotton-unsubcriptions van luisteraars met zwakke referenties oplossen, verbergt alleen het probleem.

Als je je verzameling luisteraars aan andere objecten toevoegt dan aan je waargenomen object, is je ontwerp kapot. Houd de set privé om ENCAPSULATION te ondersteunen.

Als je gelijken en hashcode van je luisteraars overschrijft, is je ontwerp kapot. Het verbergt het probleem van onnodige functieaanroepen. Voorkom onnodige telefoontjes. Na alle egaliserende gelijken en hashcode van luisteraars is het niet nodig.

In MULTITHREADING-omgevingen zet je een MONITOR op de bron "luisteraars" terwijl je erover toevoegt, verwijdert of ertegen itereert. U kunt een DEFENSIEVE KOPIE maken voordat u iterate om een ​​ConcurrentModificationException te vermijden. De iteratie hoeft dan niet te worden GESYNCHRONISEERD, maar de kopieeractie zou dat wel moeten zijn.

Elke andere vereiste moet worden aangepast of geherformuleerd om aan deze verklaringen te voldoen. Elke andere oefening zal leiden tot niet-te handhaven code, geheugenlekken vanwege een gebrek aan isolatie, onafhankelijkheid, inkapseling en helderheid.


1
2018-04-27 18:27



Je zou elke luisteraarreferentie kunnen omsluiten in a WeakReference en gebruik dan CopyOnWriteArraySet.


0
2018-01-15 15:49



U zou WeakReference kunnen uitbreiden om gelijken en hashcode te overschrijven, dan kunt u ze gebruiken in een LinkedHashSet.


0
2018-01-18 12:49