ObservableMirrors { public static <T> ObservableList<T> mirrorList(ObservableList<T> mirrored, AffinityExecutor runChangesIn) { ObservableList<T> result = FXCollections.observableArrayList(); result.setAll(mirrored); mirrored.addListener(new ListMirror<T>(result, runChangesIn)); return FXCollections.unmodifiableObservableList(result); }  static ObservableList<T> mirrorList(ObservableList<T> mirrored, AffinityExecutor runChangesIn); static ObservableMap<K, V> mirrorMap(ObservableMap<K, V> mirrored, AffinityExecutor runChangesIn); static ObservableSet<T> mirrorSet(ObservableSet<T> mirrored, AffinityExecutor runChangesIn);  }
@Test public void observableList() throws Exception { ObservableList<String> source = FXCollections.observableArrayList(); source.addAll("alpha", "beta"); ObservableList<String> dest = ObservableMirrors.mirrorList(source, gate); Queue<ListChangeListener.Change<? extends String>> changes = new LinkedList<>(); dest.addListener(changes::add); source.addAll("gamma", "delta"); assertEquals(1, gate.getTaskQueueSize()); gate.waitAndRun(); ListChangeListener.Change<? extends String> change = changes.poll(); change.next(); assertTrue(change.wasAdded()); assertEquals(2, change.getAddedSize()); assertEquals(ImmutableList.of("gamma", "delta"), change.getAddedSubList()); source.remove(3); source.add("phi"); source.remove(3); source.add("epsilon"); assertEquals(4, gate.getTaskQueueSize()); gate.waitAndRun(); gate.waitAndRun(); change = changes.poll(); change.next(); assertTrue(change.wasRemoved()); assertEquals(1, change.getRemovedSize()); assertEquals("delta", change.getRemoved().get(0)); change = changes.poll(); change.next(); assertTrue(change.wasAdded()); assertEquals("phi", change.getAddedSubList().get(0)); gate.waitAndRun(); changes.poll(); gate.waitAndRun(); changes.poll(); source.set(0, "zero"); gate.waitAndRun(); change = changes.poll(); change.next(); assertTrue(change.toString(), change.wasReplaced()); assertEquals(0, change.getFrom()); assertEquals(1, change.getTo()); assertEquals("zero", change.getAddedSubList().get(0)); }