The problem is only present when there is an enormous amount of listeners on a single property, and the list needs maintenance (ie. removing listeners). This can happen when users/nodes listen to a specific Scene or Window property (as there can be many nodes, they would all accumulate on the single Scene or Window). In most cases, it is best to avoid this somehow, but it is not always avoidable.
For properties on those kinds of objects, having a way to deal with higher than usual amounts of listeners could be useful. I've however also been thinking of other alternatives for cases where perhaps many Nodes are interested in a certain Scene or Window properties. For most cases, FX already provides something natively (but it can't be used for your own cases). A good example is the Scene property itself; it is mass updated on all nodes when the root is attached to a different scene. There is no mass listener accumulation on the root's Scene property, as each Node can listen to its own copy. Something similar applies to the "visible" properties (toggling it at a high level, also toggles it for every child node, but there is no listener registered where each Node listens to its parent's visibility). Also CSS does mass distribution of information. A style on root can affect properties on every Node in the scene, without each of these Nodes having to actively listen to something in one place. What I'm currently interested in is a mechanism to figure out whether a Node is currently part of a Scene, attached to a Window and that window is showing. Having every interested node register listeners for this would accumulate listeners on scene's window property and on window's showing property. This will not scale well when the scene changes and a bunch of nodes is replaced with new nodes (the removed nodes will want to remove their listeners, and when the list is long, this is terribly slow). However, I've thought of new solution to my problem; I could mass distribute this information by walking the scene graph, and checking for the presence of a property in the Properties list that each Node has. An interested Node can put a property in this map, and when the relevant showing state of the Scene/Window changes, I could inform all interested nodes by walking the scene graph. This is likely to be as fast as walking a listener list, but doesn't require maintaining a listener list at all (just a local property on interested Nodes). So I may have found an acceptable work-around, and perhaps the work-around is even better for this case... Still, a listener list implementation that is actually optimized specifically for its use case, can still be a useful addition. --John On 23/11/2025 10:23, Marius Hanl wrote: > This idea sounds interesting. > Unfortunately, I never looked into the numbers, e.g. I have no idea > how many listeners plain Nodes, Controls or Charts (with many Data > points) have. > That would be interesting to better understand the current situation, > and how we want to optimize the listener data structure. > > -- Marius > *Gesendet: *Samstag, 22. November 2025 um 21:35 > *Von: *"John Hendrikx" <[email protected]> > *An: *OpenJFX <[email protected]> > *Betreff: *Faster listener removal > A long time ago there was some discussion on large listener lists, and > how they perform very poorly when cleaning up (removing existing > listeners). > > A listener list basically needs to support the following operations: > > - append at end, allowing duplicates > - remove by value, removing the first (oldest) match only > - iteration > > It has no other needs. > > There was some discussion if this could be replaced with a > LinkedHashMap, but it handles duplicates subtly different and is not > very fast at iteration (lots of random memory accesses). > > So I created what is best described as an "ordered" bag. It allows > adding and removal, while maintaining order and because it is backed by > (amongst others) an ordered array, iteration is about as fast as what > ArrayList does. It has O(1) insertion, O(1) removal, O(n) iteration, > but about 3x the memory requirements (not including the listener cost > itself), unless the list is small (for small lists the overhead is only > slightly higher than ArrayList). > > Insertion is about 5x slower than ArrayList; Removal is far faster (150x > faster for a 10000 listener case); Iteration is almost equally fast. > > Because it has the exact same semantics as an (Array)List with regards > to duplicates and their removal order, it is a drop-in replacement. > > Internally it works by maintaining an ordered array (basically what > ArrayList has) which is allowed to have removal gaps that are skipped > during iteration. When the array needs to grow, it first sees if it can > consolidate the gaps before increasing the size (and it also shrinks on > demand, unlike ArrayList). Other than that, for lists above a certain > size, it maintains three additional arrays; these are used to maintain > hashed linked lists of similar elements (basically a singly linked list > per bucket, but with fast appends at the end using a tail pointer). > > When the number of listeners is low, the implementation falls back on > the simple array only (and the other 3 lists are nulled), which means > that for small lists the overhead is minimal. It's sort of a best of > both worlds thing (although there is always overhead), where it uses > minimal memory for small lists and simply scans the entire list for > removals, while for large lists it initializes additional structures to > allow for quick removals at any size. > > Thoughts? > > --John >
