On Mon, 14 Sep 2026 13:37:46 GMT, Michael Strauß <[email protected]> wrote:
> > > > * within each group (invalidation, change), the listeners are called in
> > > > the order they were registered
> > >
> > >
> > > Same as above; we could specify this as changing it now or later is
> > > likely to subtly break existing applications. In this case it also aligns
> > > with the veto behavior which would be hard to do if these lists were
> > > unordered.
> >
> >
> > Yes, I like the idea of specifying both of the above, since, as you say,
> > many applications would break if either of these changed.
>
> If we specify the invocation order, we should be careful what exactly we're
> specifying. For example, since it's become really easy to use flat-mapped
> property chains, I've been thinking that it might be an idea worth exploring
> to optimize some use cases. Since many property chains have a common prefix,
> this could be a control subscribing to something like
> `sceneProperty().flatMap(Scene::windowProperty).flatMap(Window::opacityProperty)`.
The best way to avoid the common prefix is to just make that prefix available,
but yes, this is something that could be interesting and I gave some thought to
before.
I don't think the ordering really constrains this.
> It's generally not a good idea to add a large number of listeners to a
> property, especially if the number of listeners fluctuates, as they're not
> optimized for it. With the scenario of a flat-mapped binding with a common
> prefix, this could be more efficiently implemented by a single listener on
> the prefix properties, and an internal fan-out-mechanism (for example, using
> a `Set`).
Current listener lists are indeed terrible when removals are needed frequently,
but you'd still need to track the dependents somewhere. Instead of throwing
away the standard mechanism, we could use a data structure that does allow
efficient removals. I already built one that I could contribute (see below). It
basically does exactly what listener lists need, nothing extra (meaning it is
"terrible" as a list, but perfect for random removal, append at end and
iteration).
> The question is if an ordering specification would also apply through mapped
> bindings, which would constrain some of the potential future design space.
Mapped bindings just use invalidation listeners. So if you have `a <- b <- c`
and another `a <- b <- c` then only the two bindings on `a` are in some kind of
order; `b` is unique and only has one observer; `c` has whatever observers the
user subscribed.
So if you somehow manage to automatically reuse the prefix, there is little
restriction in the middle part, and if you only have one invalidation listener
on `a` then there is no ordering issue either.
--
## `HashedSequenceList`
A resizable, ordered collection of elements that allows duplicates and provides
fast append operations and efficient removal or membership testing by equality.
This class combines an array-backed list with optional hash-based indexing
for large collections. Elements are always iterated in insertion order.
### Key characteristics:
- Appending an element is O(1) amortized.
- Removing an element by equality is O(1) on average for large lists (over ~32
elements), due to internal hash-based lookup. For smaller lists, removal scans
the array, which is not a performance concern for short lists.
- `#contains(Object)` behaves similarly: O(n) for small lists and O(1) on
average for larger lists using the hash arrays.
- Iteration over elements preserves insertion order.
- Duplicates are allowed and remain in the order they were added.
- Random access via `java.util.List#get(int)` is O(n) in the worst case (when
the list contains tombstones). Consequently, iterating over the list using an
index-based loop is O(n²), similar to `LinkedList`. Users should prefer the
{@link #iterator()} or for-each loops for O(n) total iteration performance.
- The list automatically grows as needed and may shrink when many elements are
removed, keeping memory usage proportional to its size.
### Memory usage:
- Small lists (under ~32 elements) use only the array of elements, minimizing
overhead.
- Larger lists allocate additional arrays for hash-based indexing, increasing
memory usage by roughly a factor of 2–3 compared to the element array alone.
Null elements are not permitted.
-------------
PR Comment: https://git.openjdk.org/jfx/pull/1081#issuecomment-5667341006