<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
</head>
<body>
<p>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.</p>
<p>For properties on those kinds of objects, having a way to deal
with higher than usual amounts of listeners could be useful.</p>
<p>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).<br>
</p>
<p>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.</p>
<p>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).</p>
<p>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). <br>
</p>
<p>So I may have found an acceptable work-around, and perhaps the
work-around is even better for this case...</p>
<p>Still, a listener list implementation that is actually optimized
specifically for its use case, can still be a useful addition.<br>
</p>
<p>--John<br>
</p>
<div class="moz-cite-prefix">On 23/11/2025 10:23, Marius Hanl wrote:<br>
</div>
<blockquote type="cite"
cite="mid:trinity-9e598259-a113-4203-ac1d-cc3427dc1836-1763889838104@trinity-msg-rest-webde-webde-live-d5ddb5476-t4p6r">
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<div style="font-family: 'verdana'; font-size: 12px; color: #000;"><span
style="background-color: #ffffff;">This idea sounds
interesting. <br>
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.<br>
That would be interesting to better understand the current
situation, and how we want to optimize the listener data
structure.</span></div>
<div style="font-family: 'verdana'; font-size: 12px; color: #000;"><span
style="background-color: #ffffff;"> </span></div>
<div style="font-family: 'verdana'; font-size: 12px; color: #000;"><span
style="background-color: #ffffff;">-- Marius</span></div>
<div id="sub-body-container"
style="margin: 10px 5px 5px 10px; padding: 10px 0px 10px 10px; border-left: 2px solid rgb(195, 217, 229);">
<div style="margin: 0px 0px 10px;">
<div><strong>Gesendet: </strong>Samstag, 22. November 2025 um
21:35</div>
<div><strong>Von: </strong>"John Hendrikx"
<a class="moz-txt-link-rfc2396E" href="mailto:john.hendrikx@gmail.com"><john.hendrikx@gmail.com></a></div>
<div><strong>An: </strong>OpenJFX
<a class="moz-txt-link-rfc2396E" href="mailto:openjfx-dev@openjdk.org"><openjfx-dev@openjdk.org></a></div>
<div><strong>Betreff: </strong>Faster listener removal</div>
</div>
A long time ago there was some discussion on large listener
lists, and<br>
how they perform very poorly when cleaning up (removing existing
listeners).<br>
<br>
A listener list basically needs to support the following
operations:<br>
<br>
- append at end, allowing duplicates<br>
- remove by value, removing the first (oldest) match only<br>
- iteration<br>
<br>
It has no other needs.<br>
<br>
There was some discussion if this could be replaced with a<br>
LinkedHashMap, but it handles duplicates subtly different and is
not<br>
very fast at iteration (lots of random memory accesses).<br>
<br>
So I created what is best described as an "ordered" bag. It
allows<br>
adding and removal, while maintaining order and because it is
backed by<br>
(amongst others) an ordered array, iteration is about as fast as
what<br>
ArrayList does. It has O(1) insertion, O(1) removal, O(n)
iteration,<br>
but about 3x the memory requirements (not including the listener
cost<br>
itself), unless the list is small (for small lists the overhead
is only<br>
slightly higher than ArrayList).<br>
<br>
Insertion is about 5x slower than ArrayList; Removal is far
faster (150x<br>
faster for a 10000 listener case); Iteration is almost equally
fast.<br>
<br>
Because it has the exact same semantics as an (Array)List with
regards<br>
to duplicates and their removal order, it is a drop-in
replacement.<br>
<br>
Internally it works by maintaining an ordered array (basically
what<br>
ArrayList has) which is allowed to have removal gaps that are
skipped<br>
during iteration. When the array needs to grow, it first sees
if it can<br>
consolidate the gaps before increasing the size (and it also
shrinks on<br>
demand, unlike ArrayList). Other than that, for lists above a
certain<br>
size, it maintains three additional arrays; these are used to
maintain<br>
hashed linked lists of similar elements (basically a singly
linked list<br>
per bucket, but with fast appends at the end using a tail
pointer).<br>
<br>
When the number of listeners is low, the implementation falls
back on<br>
the simple array only (and the other 3 lists are nulled), which
means<br>
that for small lists the overhead is minimal. It's sort of a
best of<br>
both worlds thing (although there is always overhead), where it
uses<br>
minimal memory for small lists and simply scans the entire list
for<br>
removals, while for large lists it initializes additional
structures to<br>
allow for quick removals at any size.<br>
<br>
Thoughts?<br>
<br>
--John<br>
<br>
</div>
</blockquote>
</body>
</html>