While a ListChangeListener can receive notifications for bulk
operations (addAll, removeAll, clear, etc.), SetChangeListener and
MapChangeListener only receive notifications for individual
add/replace/delete operations. For example, when mappings are added to
an ObservableMap with putAll(), listeners will be invoked once for
each individual mapping.

Since there is no way for a SetChangeListener/MapChangeListener to
know that more changes are coming, reacting to changes becomes
difficult and potentially inefficient if an expensive operation (like
reconfiguring the UI) is done for each individual change instead of
once for a bulk change operation.

I think we can improve the situation by adding a new method to
SetChangeListener.Change and MapChangeListener.Change:

  /**
   * Gets the next change in a series of changes.
   * <p>
   * Repeatedly calling this method allows a listener to fetch
   * all subsequent changes of a bulk set modification that would
   * otherwise be reported as repeated invocations of the listener.
   * <p>
   * After this method has been called, the current {@code Change}
   * instance is no longer valid and calling any method on it may
   * result in undefined behavior. Callers must not make any
   * assumptions about the identity of the {@code Change} instance
   * returned by this method; even if the returned instance is the
   * same as the current instance, it must be treated as a distinct
   * change.
   *
   * @return the next change, or {@code null} if there are
   *         no more changes
   */
  public Change<E> next() { return null; }


This new method allows listener implementations to fetch all
subsequent changes of a bulk operation, which can be implemented as
follows:

  set.addListener((SetChangeListener) change -> {
      do {
          // Inspect the change
          if (change.wasAdded()) {
              ...
          } else if (change.wasRemoved() {
              ...
          }
      } while ((change = change.next()) != null);
  }


The implementation is fully backwards-compatible for listeners that
are unaware of the new API. If the next() method is not called, then
all subsequent changes are delivered as usual by repeated listener
invocations.

If a listener only fetches some changes of a bulk operation (but stops
halfway through the operation), the remaining changes will also be
delivered with repeated listener invocations.

I've prepared a draft PR for this proposal:
https://github.com/openjdk/jfx/pull/1885

Reply via email to