How to concatenate observable lists in JavaFX?

女生的网名这么多〃 提交于 2019-12-13 13:26:54

问题


By concatenation I mean obtaining a new list, which listens for changes in all concatenated parts.

What is the purpose of method FXCollections#concat(ObservableList<E>... lists)? If it just merges several lists, then I see no sense to have separate method for this.

And if regard as doing what I wish then it doesn't work:

import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;

public class ConcatObservabeList {

   public static void main(String[] args) {

      ObservableList<Integer> list1 = FXCollections.observableArrayList();
      ObservableList<Integer> list2 = FXCollections.observableArrayList();


      ObservableList<Integer> concat = FXCollections.concat(list1, list2);
      concat.addListener(new ListChangeListener<Integer>() {
         public void onChanged(Change<? extends Integer> c) {
            System.out.println("changed");
         }
      });

      list1.add(12);


   }
}

回答1:


I was having the same need to concat/aggregate several ObservableLists into one list for a JavaFX LineChart. The examples I found here or on the github posted in another answer always copied all the entries from the sublists into the aggregated List on each change. For a list with many entries this seemed not very elegant.

I decided to implement my on version, which keeps track of the position of the sublists in the aggregated list and when elements in the sublists change, apply the same changes in the aggregated list. There is still room for improvement (not using a delegate List but extending a ObservableList directly, or firing the events from sublists up to the aggregated list and overriding the getters and iterators - help with that would be appreciated), but I thought I post my version here as it is, maybe it helps someone.

Code:

/**
 * This class aggregates several other Observed Lists (sublists), observes changes on those sublists and applies those same changes to the
 * aggregated list.
 * Inspired by:
 * - http://stackoverflow.com/questions/25705847/listchangelistener-waspermutated-block
 * - http://stackoverflow.com/questions/37524662/how-to-concatenate-observable-lists-in-javafx
 * - https://github.com/lestard/advanced-bindings/blob/master/src/main/java/eu/lestard/advanced_bindings/api/CollectionBindings.java
 * Posted result on: http://stackoverflow.com/questions/37524662/how-to-concatenate-observable-lists-in-javafx
 */
public class AggregatedObservableArrayList<T> {

    protected final List<ObservableList<T>> lists = new ArrayList<>();
    final private List<Integer> sizes = new ArrayList<>();
    final private List<InternalListModificationListener> listeners = new ArrayList<>();
    final protected ObservableList<T> aggregatedList = FXCollections.observableArrayList();

    public AggregatedObservableArrayList() {

    }

    /**
     * The Aggregated Observable List. This list is unmodifiable, because sorting this list would mess up the entire bookkeeping we do here.
     *
     * @return an unmodifiable view of the aggregatedList
     */
    public ObservableList<T> getAggregatedList() {
        return FXCollections.unmodifiableObservableList(aggregatedList);
    }

    public void appendList(@NotNull ObservableList<T> list) {
        assert !lists.contains(list) : "List is already contained: " + list;
        lists.add(list);
        final InternalListModificationListener listener = new InternalListModificationListener(list);
        list.addListener(listener);
        //System.out.println("list = " + list + " puttingInMap=" + list.hashCode());
        sizes.add(list.size());
        aggregatedList.addAll(list);
        listeners.add(listener);
        assert lists.size() == sizes.size() && lists.size() == listeners.size() :
              "lists.size=" + lists.size() + " not equal to sizes.size=" + sizes.size() + " or not equal to listeners.size=" + listeners.size();
    }

    public void prependList(@NotNull ObservableList<T> list) {
        assert !lists.contains(list) : "List is already contained: " + list;
        lists.add(0, list);
        final InternalListModificationListener listener = new InternalListModificationListener(list);
        list.addListener(listener);
        //System.out.println("list = " + list + " puttingInMap=" + list.hashCode());
        sizes.add(0, list.size());
        aggregatedList.addAll(0, list);
        listeners.add(0, listener);
        assert lists.size() == sizes.size() && lists.size() == listeners.size() :
              "lists.size=" + lists.size() + " not equal to sizes.size=" + sizes.size() + " or not equal to listeners.size=" + listeners.size();
    }

    public void removeList(@NotNull ObservableList<T> list) {
        assert lists.size() == sizes.size() && lists.size() == listeners.size() :
              "lists.size=" + lists.size() + " not equal to sizes.size=" + sizes.size() + " or not equal to listeners.size=" + listeners.size();
        final int index = lists.indexOf(list);
        if (index < 0) {
            throw new IllegalArgumentException("Cannot remove a list that is not contained: " + list + " lists=" + lists);
        }
        final int startIndex = getStartIndex(list);
        final int endIndex = getEndIndex(list, startIndex);
        // we want to find the start index of this list inside the aggregated List. End index will be start + size - 1.
        lists.remove(list);
        sizes.remove(index);
        final InternalListModificationListener listener = listeners.remove(index);
        list.removeListener(listener);
        aggregatedList.remove(startIndex, endIndex + 1); // end + 1 because end is exclusive
        assert lists.size() == sizes.size() && lists.size() == listeners.size() :
              "lists.size=" + lists.size() + " not equal to sizes.size=" + sizes.size() + " or not equal to listeners.size=" + listeners.size();
    }

    /**
     * Get the start index of this list inside the aggregated List.
     * This is a private function. we can safely asume, that the list is in the map.
     *
     * @param list the list in question
     * @return the start index of this list in the aggregated List
     */
    private int getStartIndex(@NotNull ObservableList<T> list) {
        int startIndex = 0;
        //System.out.println("=== searching startIndex of " + list);
        assert lists.size() == sizes.size() : "lists.size=" + lists.size() + " not equal to sizes.size=" + sizes.size();
        final int listIndex = lists.indexOf(list);
        for (int i = 0; i < listIndex; i++) {
            final Integer size = sizes.get(i);
            startIndex += size;
            //System.out.println(" startIndex = " + startIndex + " added=" + size);
        }
        //System.out.println("startIndex = " + startIndex);
        return startIndex;
    }

    /**
     * Get the end index of this list inside the aggregated List.
     * This is a private function. we can safely asume, that the list is in the map.
     *
     * @param list       the list in question
     * @param startIndex the start of the list (retrieve with {@link #getStartIndex(ObservableList)}
     * @return the end index of this list in the aggregated List
     */
    private int getEndIndex(@NotNull ObservableList<T> list, int startIndex) {
        assert lists.size() == sizes.size() : "lists.size=" + lists.size() + " not equal to sizes.size=" + sizes.size();
        final int index = lists.indexOf(list);
        return startIndex + sizes.get(index) - 1;
    }

    private class InternalListModificationListener implements ListChangeListener<T> {

        @NotNull
        private final ObservableList<T> list;

        public InternalListModificationListener(@NotNull ObservableList<T> list) {
            this.list = list;
        }

        /**
         * Called after a change has been made to an ObservableList.
         *
         * @param change an object representing the change that was done
         * @see Change
         */
        @Override
        public void onChanged(Change<? extends T> change) {
            final ObservableList<? extends T> changedList = change.getList();
            final int startIndex = getStartIndex(list);
            final int index = lists.indexOf(list);
            final int newSize = changedList.size();
            //System.out.println("onChanged for list=" + list + " aggregate=" + aggregatedList);
            while (change.next()) {
                final int from = change.getFrom();
                final int to = change.getTo();
                //System.out.println(" startIndex=" + startIndex + " from=" + from + " to=" + to);
                if (change.wasPermutated()) {
                    final ArrayList<T> copy = new ArrayList<>(aggregatedList.subList(startIndex + from, startIndex + to));
                    //System.out.println("  permutating sublist=" + copy);
                    for (int oldIndex = from; oldIndex < to; oldIndex++) {
                        int newIndex = change.getPermutation(oldIndex);
                        copy.set(newIndex - from, aggregatedList.get(startIndex + oldIndex));
                    }
                    //System.out.println("  permutating done sublist=" + copy);
                    aggregatedList.subList(startIndex + from, startIndex + to).clear();
                    aggregatedList.addAll(startIndex + from, copy);
                } else if (change.wasUpdated()) {
                    // do nothing
                } else {
                    if (change.wasRemoved()) {
                        List<? extends T> removed = change.getRemoved();
                        //System.out.println("  removed= " + removed);
                        // IMPORTANT! FROM == TO when removing items.
                        aggregatedList.remove(startIndex + from, startIndex + from + removed.size());
                    }
                    if (change.wasAdded()) {
                        List<? extends T> added = change.getAddedSubList();
                        //System.out.println("  added= " + added);
                        //add those elements to your data
                        aggregatedList.addAll(startIndex + from, added);
                    }
                }
            }
            // update the size of the list in the map
            //System.out.println("list = " + list + " puttingInMap=" + list.hashCode());
            sizes.set(index, newSize);
            //System.out.println("listSizesMap = " + sizes);
        }

    }

    public String dump(Function<T, Object> function) {
        StringBuilder sb = new StringBuilder();
        sb.append("[");
        aggregatedList.forEach(el -> sb.append(function.apply(el)).append(","));
        final int length = sb.length();
        sb.replace(length - 1, length, "");
        sb.append("]");
        return sb.toString();
    }
}

jUnit Test:

/**
 * Testing the AggregatedObservableArrayList
 */
public class AggregatedObservableArrayListTest {


    @Test
    public void testObservableValue() {
        final AggregatedObservableArrayList<IntegerProperty> aggregatedWrapper = new AggregatedObservableArrayList<>();
        final ObservableList<IntegerProperty> aggregatedList = aggregatedWrapper.getAggregatedList();
        aggregatedList.addListener((Observable observable) -> {
            System.out.println("observable = " + observable);
        });

        final ObservableList<IntegerProperty> list1 = FXCollections.observableArrayList();
        final ObservableList<IntegerProperty> list2 = FXCollections.observableArrayList();
        final ObservableList<IntegerProperty> list3 = FXCollections.observableArrayList();

        list1.addAll(new SimpleIntegerProperty(1), new SimpleIntegerProperty(2), new SimpleIntegerProperty(3), new SimpleIntegerProperty(4),
                     new SimpleIntegerProperty(5));
        list2.addAll(new SimpleIntegerProperty(10), new SimpleIntegerProperty(11), new SimpleIntegerProperty(12), new SimpleIntegerProperty(13),
                     new SimpleIntegerProperty(14), new SimpleIntegerProperty(15));
        list3.addAll(new SimpleIntegerProperty(100), new SimpleIntegerProperty(110), new SimpleIntegerProperty(120), new SimpleIntegerProperty(130),
                     new SimpleIntegerProperty(140), new SimpleIntegerProperty(150));

        // adding list 1 to aggregate
        aggregatedWrapper.appendList(list1);
        assertEquals("wrong content", "[1,2,3,4,5]", aggregatedWrapper.dump(ObservableIntegerValue::get));

        // removing elems from list1
        list1.remove(2, 4);
        assertEquals("wrong content", "[1,2,5]", aggregatedWrapper.dump(ObservableIntegerValue::get));

        // adding second List
        aggregatedWrapper.appendList(list2);
        assertEquals("wrong content", "[1,2,5,10,11,12,13,14,15]", aggregatedWrapper.dump(ObservableIntegerValue::get));

        // removing elems from second List
        list2.remove(1, 3);
        assertEquals("wrong content", "[1,2,5,10,13,14,15]", aggregatedWrapper.dump(ObservableIntegerValue::get));

        // replacing element in first list
        list1.set(1, new SimpleIntegerProperty(3));
        assertEquals("wrong content", "[1,3,5,10,13,14,15]", aggregatedWrapper.dump(ObservableIntegerValue::get));

        // adding third List
        aggregatedWrapper.appendList(list3);
        assertEquals("wrong content", "[1,3,5,10,13,14,15,100,110,120,130,140,150]", aggregatedWrapper.dump(ObservableIntegerValue::get));

        // emptying second list
        list2.clear();
        assertEquals("wrong content", "[1,3,5,100,110,120,130,140,150]", aggregatedWrapper.dump(ObservableIntegerValue::get));

        // adding new elements to second list
        list2.addAll(new SimpleIntegerProperty(203), new SimpleIntegerProperty(202), new SimpleIntegerProperty(201));
        assertEquals("wrong content", "[1,3,5,203,202,201,100,110,120,130,140,150]", aggregatedWrapper.dump(ObservableIntegerValue::get));

        // sorting list2. this results in permutation
        list2.sort((o1, o2) -> o1.getValue().compareTo(o2.getValue()));
        assertEquals("wrong content", "[1,3,5,201,202,203,100,110,120,130,140,150]", aggregatedWrapper.dump(ObservableIntegerValue::get));

        // removing list2 completely
        aggregatedWrapper.removeList(list2);
        assertEquals("wrong content", "[1,3,5,100,110,120,130,140,150]", aggregatedWrapper.dump(ObservableIntegerValue::get));

        // updating one integer value in list 3
        SimpleIntegerProperty integer = (SimpleIntegerProperty) list3.get(0);
        integer.set(1);
        assertEquals("wrong content", "[1,3,5,1,110,120,130,140,150]", aggregatedWrapper.dump(ObservableIntegerValue::get));

        // prepending list 2 again
        aggregatedWrapper.prependList(list2);
        assertEquals("wrong content", "[201,202,203,1,3,5,1,110,120,130,140,150]", aggregatedWrapper.dump(ObservableIntegerValue::get));

    }

}



回答2:


A ListChangeListener added to an ObservableList sees certain specific changes made to the list as a whole. The overhead of also listening to any ancestor lists is considerable, as seen in the API cited here. Because FXCollections.concat() simply copies references from the source lists to the destination's backing list, a listener added to concat will see changes made to concat; it will not see changes to list1 or list2.

If you don't need to create a new ObservableList for some other reason, aggregate the lists in a way that allows you to add the same listener to each.

Console:

changed { [42] added at 0 }

Code:

import java.util.ArrayList;
import java.util.List;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;

/**
 * @see https://stackoverflow.com/a/37527245/230513
 */
public class ObservableListAggregate {

    public static void main(String[] args) {
        ObservableList<Integer> list1 = FXCollections.observableArrayList();
        ObservableList<Integer> list2 = FXCollections.observableArrayList();
        Aggregate<ObservableList<Integer>> aggregate = new Aggregate(list1, list2);
        aggregate.addListener(new ListChangeListener<ObservableList<Integer>>() {
            @Override
            public void onChanged(ListChangeListener.Change<? extends ObservableList<Integer>> c) {
                System.out.println("changed " + c);
            }
        });
        list1.add(42);
    }

    private static class Aggregate<T> {

        List<ObservableList<T>> lists = new ArrayList<>();

        public Aggregate(ObservableList<T>... lists) {
            for (ObservableList<T> list : lists) {
                this.lists.add(list);
            }
        }

        public final void addListener(ListChangeListener<? super T> listener) {
            for (ObservableList<T> list : lists) {
                list.addListener(listener);
            }
        }

        public final void removeListener(ListChangeListener<? super T> listener) {
            for (ObservableList<T> list : lists) {
                list.removeListener(listener);
            }
        }
    }
}

To see changes to the individual list elements, use an ObservableList<Observable>, such as ObservableList<IntegerProperty>. In the example below, note that ip, added to list1, is the same IntegerProperty later modified in concat.

Console:

IntegerProperty [value: 42]
concat changed { [IntegerProperty [value: 2147483647]] added at 1 }

Code:

import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;

public class ConcatObservabeList {

    public static void main(String[] args) {

        ObservableList<IntegerProperty> list1 = FXCollections.observableArrayList();
        IntegerProperty ip = new SimpleIntegerProperty(0);
        ip.addListener(System.out::println);
        list1.add(ip);
        ObservableList<IntegerProperty> list2 = FXCollections.observableArrayList();
        ObservableList<IntegerProperty> concat = FXCollections.concat(list1, list2);
        concat.get(0).setValue(42);
        concat.addListener(new ListChangeListener<IntegerProperty>() {
            @Override
            public void onChanged(ListChangeListener.Change<? extends IntegerProperty> c) {
                System.out.println("concat changed " + c);
            }
        });
        concat.add(new SimpleIntegerProperty(Integer.MAX_VALUE));
    }
}



回答3:


I stumbled across the same problem, because the method FXCollections.concat(...) did not change on changes of the source lists, which was a requirement for my use case.

Since the other answers to this question seem like a little overkill to me, I'll add my own solution, which has two major limitations:

  • The resulting ObservableList will be read-only to the contents of the source lists.
  • The resulting ObservableList will be a new instance on each change, so that possible UI elements based on them will be newly created on every change.

As any ListBinding computes an ObservableList as its value and implements (and therefor is a) ObservableList at the same time, it can be easily used to enhance the default concat method to update on changes of the source lists:

@SafeVarargs
public static <T> ObservableList<T> concat(ObservableList<T>... sources) {
    return new ListBinding<T>() {

        {
            bind(sources);
        }

        @Override
        protected ObservableList<T> computeValue() {
            return FXCollections.concat(sources);
        }
    };
}



回答4:


I had the same issue and have created a helper for this use case in the library advanced-bindings (javadoc of the helper method).




回答5:


I also had the same problem. Finally I made a list of ObservableList:

import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;

import javafx.beans.InvalidationListener;
import javafx.beans.Observable;
import javafx.beans.WeakInvalidationListener;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.collections.WeakListChangeListener;

/**
 * Read only view on a list of {@link ObservableList}
 *
 * @author Marcel Heckel
 */
public class CompositeObservableList<T> extends AbstractList<T> implements ObservableList<T>
{
    protected List<ObservableList<T>>             lists;

    protected List<ListChangeListener<? super T>> listChangeListeners          = new ArrayList<>();

    protected List<InvalidationListener>          invalidationListeners        = new ArrayList<>();

    protected InvalidationListener                internalInvalidationListener = this::invalidated;

    protected ListChangeListener<T>               internalListChangeListener   = this::onChanged;

    protected WeakInvalidationListener            weakInvalidationListener     = new WeakInvalidationListener(
        internalInvalidationListener);

    protected WeakListChangeListener<T>           weakListChangeListener       = new WeakListChangeListener<>(
        internalListChangeListener);

    public CompositeObservableList()
    {
        this.lists = new ArrayList<>();
    }

    public CompositeObservableList(List<ObservableList<T>> lists)
    {
        this.lists = lists;

        for (ObservableList<T> l : lists)
        {
            l.addListener(weakInvalidationListener);
            l.addListener(weakListChangeListener);
        }
    }

    public void addObservableList(ObservableList<T> l)
    {
        lists.add(l);
        l.addListener(weakInvalidationListener);
        l.addListener(weakListChangeListener);
    }

    /** remove listeners and clears the internal list */
    public void clearLists()
    {
        for (ObservableList<T> l : lists)
        {
            l.removeListener(weakInvalidationListener);
            l.removeListener(weakListChangeListener);
        }
        lists.clear();
    }

    ///////////////////////////////////////
    // listeners

    private void invalidated(Observable observable)
    {
        for (InvalidationListener l : invalidationListeners)
            l.invalidated(CompositeObservableList.this);
    }

    private void onChanged(ListChangeListener.Change<? extends T> c)
    {
        int idx = getStartIndexOfListReference(c.getList());
        assert (idx >= 0);
        if (idx < 0)
            return;

        c = new IndexOffsetChange<>(CompositeObservableList.this, idx, c);

        for (ListChangeListener<? super T> l : listChangeListeners)
        {
            l.onChanged(c);
        }
    }

    private int getStartIndexOfListReference(ObservableList<?> l)
    {
        int startIndex = 0;
        for (int i = 0; i < lists.size(); i++ )
        {
            if (l == lists.get(i))
                return startIndex;
            startIndex += l.size();
        }
        return -1;
    }

    ////////////////////////////////////////

    @Override
    public int size()
    {
        int size = 0;
        for (Collection<T> c : lists)
            size += c.size();
        return size;
    }

    @Override
    public boolean isEmpty()
    {
        for (Collection<T> c : lists)
            if ( !c.isEmpty())
                return false;
        return true;
    }

    @Override
    public boolean contains(Object obj)
    {
        for (Collection<T> c : lists)
            if (c.contains(obj))
                return true;
        return false;
    }

    @Override
    public boolean containsAll(Collection<?> c)
    {
        for (Object ele : c)
        {
            if ( !this.contains(ele))
                return false;
        }
        return true;
    }

    @Override
    public int indexOf(Object o)
    {
        int index = 0;
        for (List<T> l : lists)
        {
            int i = l.indexOf(o);
            if (i >= 0)
                return index + i;
            index += l.size();
        }
        return -1;
    }

    @Override
    public T get(int index)
    {
        if (index < 0)
            throw new IndexOutOfBoundsException("index: " + index + " - size: " + size());

        for (List<T> l : lists)
        {
            if (l.size() > index)
                return l.get(index);
            index -= l.size();
        }
        throw new IndexOutOfBoundsException("index: " + index + " - size: " + size());
    }

    @Override
    public Iterator<T> iterator()
    {
        return new Iterator<T>()
            {
                Iterator<T>                 currentIterator = null;

                Iterator<ObservableList<T>> listsIterator   = lists.iterator();

                @Override
                public boolean hasNext()
                {
                    while (true)
                    {
                        if (currentIterator != null && currentIterator.hasNext())
                            return true;
                        if ( !listsIterator.hasNext())
                            return false;
                        currentIterator = listsIterator.next().iterator();
                    }
                }

                @Override
                public T next()
                {
                    if ( !hasNext())
                        throw new NoSuchElementException();
                    return currentIterator.next();
                }

                @Override
                public void remove()
                {
                    throw new UnsupportedOperationException();
                }

            };
    }

    // editing methods

    @Override
    public boolean add(T obj)
    {
        throw new UnsupportedOperationException();
    }

    @Override
    public boolean remove(Object obj)
    {
        throw new UnsupportedOperationException();
    }

    @Override
    public boolean addAll(Collection<? extends T> c)
    {
        throw new UnsupportedOperationException();
    }

    @Override
    public boolean removeAll(Collection<?> c)
    {
        throw new UnsupportedOperationException();
    }

    @Override
    public boolean retainAll(final Collection<?> c)
    {
        throw new UnsupportedOperationException();
    }

    @Override
    public void clear()
    {
        throw new UnsupportedOperationException();
    }

    @Override
    public boolean addAll(int index, Collection<? extends T> c)
    {

        throw new UnsupportedOperationException();
    }

    @Override
    public T set(int index, T element)
    {
        throw new UnsupportedOperationException();
    }

    @Override
    public void add(int index, T element)
    {
        throw new UnsupportedOperationException();
    }

    @Override
    public T remove(int index)
    {
        throw new UnsupportedOperationException();
    }

    // editing methods of ObservableList list

    @Override
    public boolean addAll(T... elements)
    {
        throw new UnsupportedOperationException();
    }

    @Override
    public boolean setAll(T... elements)
    {
        throw new UnsupportedOperationException();
    }

    @Override
    public boolean setAll(Collection<? extends T> c)
    {
        throw new UnsupportedOperationException();
    }

    @Override
    public boolean removeAll(T... elements)
    {
        throw new UnsupportedOperationException();
    }

    @Override
    public boolean retainAll(T... elements)
    {
        throw new UnsupportedOperationException();
    }

    @Override
    public void remove(int from, int to)
    {
        throw new UnsupportedOperationException();
    }

    /////////////////////////////

    @Override
    public void addListener(InvalidationListener listener)
    {
        invalidationListeners.add(listener);
    }

    @Override
    public void removeListener(InvalidationListener listener)
    {
        invalidationListeners.remove(listener);
    }

    @Override
    public void addListener(ListChangeListener<? super T> listener)
    {
        listChangeListeners.add(listener);

    }

    @Override
    public void removeListener(ListChangeListener<? super T> listener)
    {
        listChangeListeners.remove(listener);
    }

    ////////////////

    private static class IndexOffsetChange<T> extends ListChangeListener.Change<T>
    {
        private final int                                    indexOffset;

        private final ListChangeListener.Change<? extends T> delegate;

        public IndexOffsetChange(ObservableList<T> list, final int indexOffset,
            ListChangeListener.Change<? extends T> c)
        {
            super(list);
            this.indexOffset = indexOffset;
            this.delegate = c;
        }

        @Override
        public boolean next()
        {
            return delegate.next();
        }

        @Override
        public void reset()
        {
            delegate.reset();
        }

        @Override
        public int getFrom()
        {
            return delegate.getFrom() + indexOffset;
        }

        @Override
        public int getTo()
        {
            return delegate.getTo() + indexOffset;
        }

        @Override
        public boolean wasPermutated()
        {
            return delegate.wasPermutated();
        }

        @Override
        public int getPermutation(int i)
        {
            return indexOffset + super.getPermutation(i - indexOffset);
        }

        @Override
        protected int[] getPermutation()
        {
            return null;
        }

        @SuppressWarnings("unchecked")
        @Override
        public List<T> getAddedSubList()
        {
            return (List<T>) delegate.getAddedSubList();
        }

        @Override
        public int getAddedSize()
        {
            return delegate.getAddedSize();
        }

        @Override
        public boolean wasAdded()
        {
            return delegate.wasAdded();
        }

        @SuppressWarnings("unchecked")
        @Override
        public List<T> getRemoved()
        {
            return (List<T>) delegate.getRemoved();
        }

        @Override
        public int getRemovedSize()
        {
            return delegate.getRemovedSize();
        }

        @Override
        public boolean wasRemoved()
        {
            return delegate.wasRemoved();
        }

        @Override
        public boolean wasReplaced()
        {
            return delegate.wasReplaced();
        }

        @Override
        public boolean wasUpdated()
        {
            return delegate.wasUpdated();
        }

    }

}


来源:https://stackoverflow.com/questions/37524662/how-to-concatenate-observable-lists-in-javafx

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!