Basics of JavaFX sequence implementation

This article introduces the core classes used for the JavaFX sequence implementation. Note that these are internal classes and interfaces, not a supported API. This is purely for your information and edification: The goal is to help you get a mental model of what is under the hood, and the approximate cost of the various sequence operations.

The Sequence interface

A JavaFX sequence type T[] is implemented using classes that implement the Sequence<T> interface:

public interface Sequence<T> {
  /** Number of items in sequence. */
  public int size();

  public T get(int position);

  /** Copy elements from sequence into an array. */
  public void toArray(int sourceOffset, int length, Object[] array, int destOffset);

  /** Get this[startPos..<endPos]. */
  public Sequence<T> getSlice(int startPos, int endPos);

  /** What to return when an index is out-of-range. */
  T getDefaultValue();

  ... a few more ...;
}

All (existing) Sequence concrete classes extend AbstractSequence, which implements various utility methods:

public class AbstractSequence<T> implements Sequence<T> {
  ... default implementations of some methods ...
}

(Idea to consider: We might want to merge Sequence and AbstractSequence. That would save a little bit of static footprint. It should also slightly improve performance, since virtual method calls are usually more efficient than interface method calls, especially on not-so-smart VMs.)

The default sequence implementation is ArraySequence, which (surprise!) uses a Java array. The following is highly simplified; we'll go into ArraySequence in more detail in a later note.

public class ArraySequence<T> extends AbstractSequence<T> {
    T[] array;
    // Warning - this is simplified!
    public T get(int position) {
         if (position < 0 || position >= array.length)
             return getDefaultValue();
         return array[position];
    }
    ...
}

There are a few special Sequence classes. SingletonSequence is an optimization for single-item sequences. IntRangeSequence (and the similar NumberRangeSequence) are used to implement [start..<end] ranges:

public class IntRangeSequence extends AbstractSequence<Integer> {
    int start, step, size;
    ...;
    public Integer get(int position) {
        if (position < 0 || position >= size)
            return getDefaultValue();
        else
            return (start + position * step);
    }
}

Finally, SubSequence is used for sequence slices:

public class SubSequence<Integer> extends AbstractSequence<Integer> {
    public T get(int position) {
        if (position < 0 || position >= size)
            return getDefaultValue();
        else
            return sequence.get(startPos + step * position);
    }
    ...
}

The JavaFX type T[] is actually mapped to the Java type Sequence<? extends T>, where the ? extends T specifies a wildcard. This is to support co-variance of sequence types. You see this below.

What about sequences of primitive types, such as Integer, which corresponds to the Java int type? That deserves a separate article.

A named mutable variable is represented by SequenceVariable. It stores the current value, which is a sequence value, of type Sequence<? extends T>.

class SequenceVariable<T> {
    protected Sequence<? extends T> $value;
    public Sequence<? extends T> getAsSequence() { return $value; }

    public Sequence<? extends T> setAsSequence (Sequence newValue) {
        $value = newValue;
        ... handle triggers and dependencies ...
    }
  
    public void insertBefore(T value, int position) {
        replaceSlice(position, position, value);
    }

    public void replaceSlice(int startPos, int endPos, T newValue) {
      ... interesting stuff ...
    }

    public void replaceSlice(int startPos, int endPos, Sequence newValue) {
      ... interesting stuff ...
    }
   ...
}

All of the sequence insert/delete/replace operations map down to either replaceSlice(int startPos, int endPos, T newValue) or replaceSlice(int startPos, int endPos, Sequence<? extends T> newValues), where the former can be viewed as optimizations of the latter.

These operations for updating sequence variables are interesting and tricky enough that they deserve separate articles JavaFX-sequence-updating and JavaFX-sequence-triggers.

Tags: