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
>

Reply via email to