RFR: 8367439: Bulk change notifications for ObservableSet and ObservableMap [v3]
Michael Strauß
mstrauss at openjdk.org
Thu Oct 23 23:31:04 UTC 2025
On Thu, 23 Oct 2025 19:25:58 GMT, Andy Goryachev <angorya at openjdk.org> wrote:
>> Michael Strauß has updated the pull request with a new target base due to a merge or a rebase. The incremental webrev excludes the unrelated changes brought in by the merge/rebase. The pull request contains eight additional commits since the last revision:
>>
>> - Merge branch 'master' into feature/bulk-listeners
>> - remove unused variable
>> - Don't repeatedly call backingSet.size()
>> - Separate code paths for Change/IterableChange
>> - Use MapListenerHelper in PlatformPreferences to support bulk change notifications
>> - Factor out IterableSetChange/IterableMapChange implementations
>> - add tests, documentation
>> - Implementation of bulk change listeners for ObservableSet and ObservableMap
>
> modules/javafx.base/src/main/java/com/sun/javafx/collections/ObservableMapWrapper.java line 148:
>
>> 146: change.nextAdded(key, newValue);
>> 147: } else {
>> 148: V oldValue = backingMap.get(key);
>
> `containsKey()` followed by a `get()` - do you think it's possible to optimize this to avoid looking up twice? maybe use `get() == null` ?
This is possible if we know that the map doesn't contain null mappings. But since `ObservableMapWrapper` is a wrapper around an arbitray `Map`, we need to account for null mappings. As a consequence of that, we don't know if `map.get(key) == null` means that no mapping is present, or if a mapping is present but its value is `null`.
> modules/javafx.base/src/main/java/com/sun/javafx/collections/ObservableMapWrapper.java line 795:
>
>> 793: }
>> 794:
>> 795: private class SimpleChange extends MapChangeListener.Change<K,V> {
>
> could this and `BulkChange` classes made static?
Yes, I can do that for the existing `Change` implementations, but it's not directly related to bulk change notifications. Maybe another PR would be better.
> modules/javafx.base/src/main/java/com/sun/javafx/collections/ObservableSetWrapper.java line 532:
>
>> 530: @Override
>> 531: public void clear() {
>> 532: if (backingSet.size() > 1) {
>
> minor suggestion:
>
> switch(backing.size()) {
> case 0:
> break;
> case 1:
> ..
> default:
> ..
> }
>
> though the compiler is probably going to optimize the second call to `size()` out.
I've factored out the repeated call to `backingSet.size()`, and reordered the branches such that == 1 comes before > 1. This reads more cleanly now, and I think is also a bit better than a switch block.
> modules/javafx.base/src/main/java/com/sun/javafx/collections/SetListenerHelper.java line 166:
>
>> 164: Thread.currentThread().getUncaughtExceptionHandler().uncaughtException(Thread.currentThread(), e);
>> 165: }
>> 166: } while (change instanceof IterableSetChange<? extends E> c && c.nextChange());
>
> this is also minor: we are doing instanceof in a loop, perhaps we just need to add a second code path:
>
> if(change instanceof IterableSetChange c) {
> try {...}
> } else {
> try {..}
Yes, changed this here and also in `MapListenerHelper`.
-------------
PR Review Comment: https://git.openjdk.org/jfx/pull/1885#discussion_r2457888231
PR Review Comment: https://git.openjdk.org/jfx/pull/1885#discussion_r2457888283
PR Review Comment: https://git.openjdk.org/jfx/pull/1885#discussion_r2457895737
PR Review Comment: https://git.openjdk.org/jfx/pull/1885#discussion_r2457897995
More information about the openjfx-dev
mailing list