如果我有一个原始列表[1,2,3],并将list []绑定到它,则绑定列表[1,3]需要匹配它.
如果原始列表的比较器被交换,原始列表现在读取[3,1],我如何使绑定列表跟随?
/** * Binds a source list's elements to a destination list. Any changes made in * the source list will reflect in the destination list. * * @param <SRC> The source list's object type. * @param <DEST> The destination list's object type. * @param dest The destination list that will be bound to the src list. * @param src The source list to watch for changes,and propagate up to the * destination list. * @param transformer A function that will transform a source list data * type,A,into a destination list data type,B. */ public static <SRC,DEST> void bindLists( ObservableList<DEST> dest,ObservableList<SRC> src,Function<? super SRC,? extends DEST> transformer) { /*Add the initial data into the destination list.*/ for (SRC a : src) { dest.add(transformer.apply(a)); } /*Watch for future data to add to the destination list. Also watch for removal of data form the source list to remove its respective item in the destination list.*/ src.addListener((ListChangeListener.Change<? extends SRC> c) -> { while (c.next()) { if (c.wasPermutated()) { /*How do you handle permutations? Do you remove and then add,or add and then remove,or use set,or use a copy arraylist and set the right indices? Removing/adding causes concurrent modifications.*/ for (int oldIndex = c.getFrom(); oldIndex < c.getTo(); oldIndex++) { int newIndex = c.getPermutation(oldIndex); dest.remove(oldIndex); dest.add(newIndex,dest.get(oldIndex)); } } else if (c.wasUpdated()) { } else { /*Respond to removed data.*/ for (SRC item : c.getRemoved()) { int from = c.getFrom(); dest.remove(from); } /*Respond to added data.*/ for (SRC item : c.getAddedSubList()) { int indexAdded = src.indexOf(item); dest.add(indexAdded,transformer.apply(item)); } } } }); }
解决方法
从概念上讲,你得到的是一系列受影响的元素,以及一个包含一些数字的数组,这些数字表示每个元素的移动位置.我想你明白了.在您的代码中,
newIndex = getPermutation(oldIndex);
这意味着要在oldIndex处的元素需要移动到newIndex.皱纹是,如果你直接进行移动,你可能会覆盖尚未移动的元素.我认为解决这个问题的最简单方法是制作受影响的子范围的副本,然后单步执行排列数组并将副本中的元素移动到新的位置.执行此操作的代码是:
int from = c.getFrom(); int to = c.getTo(); List<DEST> copy = new ArrayList<>(dest.subList(from,to)); for (int oldIndex = from; oldIndex < to; oldIndex++) { int newIndex = c.getPermutation(oldIndex); dest.set(newIndex,copy.get(oldIndex - from)); }
这是一种排列,因此每个元素都会在某处结束,而不会添加或删除任何元素.这意味着您不必复制列表范围,并且可以在仅使用单个临时空间元素的同时在移动链之后一次移动一个元素.可能存在多个链循环,因此您必须检测并处理它.这听起来很复杂.我会把它留给另一个回答者. :-)对于我的钱,复制受影响的范围简单易懂.
正常列表操作不会触发排列和更新的更改模式.如果查看javafx.collections.ObservableListBase,您可以看到列表实现可用于构建特定更改信息的协议.如果实现向nextPermutation或nextUpdate方法提供正确的信息,它将触发这些更多其他更改模式.我不确定在JavaFX中可能触发它们的是什么.例如,更改节点堆叠顺序的Node.toFront()和Node.toBack()方法可能会生成排列更改,但它们似乎不会.我不知道任何会产生更新更改的内容.
在语义上,我认为更新更改意味着列表范围内的元素已更改,但列表的长度保持不变.这与“替换”改变模式形成对比,其中一系列元素可能被不同数量的元素替换.也可能是更新更改意味着元素本身没有被替换 – 也就是说,列表内容没有改变 – 只是元素的内部状态已经改变.