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