shuffling observeList触发不正确的更改通知

oymdgrw7  于 2021-06-27  发布在  Java
关注(0)|答案(2)|浏览(302)

方法 FXCollections.shuffle() 仅限火灾 wasRemoved 更改通知。我们可能知道,洗牌不仅仅是删除,而是删除和添加。
在文档中我们可以看到:
洗牌可观察列表中的所有元素。在列表上只触发一个更改通知。
如果我没弄错的话,一个单一的改变可以同时包含这两个方面 wasAdded 以及 wasRemoved . 真可惜 wasPermutated 不是用默认值触发的 ObservableList fxapi(或者是?)。
测试代码:

public class SimpleMain {

    public static void main(String[] args) {
        ObservableList<Integer> integers = FXCollections.observableArrayList(1, 2, 3, 4);
        integers.addListener(initListener());
        FXCollections.shuffle(integers);
    }

    private static ListChangeListener<Integer> initListener() {
        return change -> {
            while (change.next()) {
                if (change.wasPermutated()) {
                    System.out.println("wasPermutated");
                } else if (change.wasRemoved()) {
                    System.out.println("wasRemoved");
                } else if (change.wasAdded()) {
                    System.out.println("wasAdded");
                }
            }
        };
    }

}
hxzsmxv2

hxzsmxv21#

我在while循环中向listchangelistener添加了一行:

System.out.println("change: "+change);

我得到的结果是:

change: { [1, 2, 3, 4] replaced by [4, 2, 3, 1] at 0 }
wasRemoved

这暗示了你的错误。字符串表示中使用的动词是“replaced”。去掉侦听器中的'else's并为wasreplaced()添加一个检查,您将看到这正是您得到的结果。

7nbnzgx9

7nbnzgx92#

问题

您的代码假定 wasAdded() 以及 wasRemoved() 是相互排斥的,不管你是不是有意的。这种假设是错误的。如果替换了一个或多个连续元素,那么这两个方法都将返回 true .
请记住 Change 对象和“更改”。单身汉 Change 示例可以携带多个更改。当文件上说:
在列表上只触发一个更改通知。
它不是说只有一个 Change 对象将被发送到 ListChangeListener . 它说的是 Change 对象将只携带一个更改。换言之 Change#next() 方法将只返回 true 为了第一次的召唤 while 循环将只循环一次。

解决方案

你需要重写你的代码 wasAdded() 以及 wasRemoved() 两者都可以 true . 例如,下面是一个侦听器,它检查所有类型的更改:

private static ListChangeListener<Integer> initListener() {
  return change -> {
    while (change.next()) {
      if (change.wasPermutated()) {
        System.out.println("wasPermutated");
      } else if (change.wasUpdated()) {
        System.out.println("wasUpdated");
      } else if (change.wasReplaced()) {
        System.out.println("wasReplaced");
      } else if (change.wasRemoved()) {
        System.out.println("wasRemoved");
      } else { // only other change type is "added"
        System.out.println("wasAdded");
      }
    }
  };
}

以上用途 wasReplaced() 这和 wasAdded() && wasRemoved() . 注意,检查 wasReplaced() 必须在任何一个之前发生 wasRemoved() 或者 wasAdded() 如果使用if-else-if结构。否则上面的代码将遇到与您的代码相同的问题。
如果将上述内容插入代码并运行,您将看到以下输出:

wasReplaced

文件 ListChangeListener.Change 给出了如何实现 ListChangeListener .
注意文档中的示例不检查 wasReplaced() 明确地。相反,它在最终的 else 块(后 wasPermutated() 以及 wasUpdated() 返回 false ). 这是可能的,因为 getRemoved() 以及 getAddedSubList() 如果没有删除或添加元素,则返回空列表。如果处理任何删除的元素,然后再处理任何添加的元素,效果通常与特别处理替换的效果相同。但是,根据您的用例,专门处理替换可能是有益的。

为什么不排列呢?

他们实施计划的方式 shuffle 方法是:

public static void shuffle(ObservableList list, Random rnd) {
    Object newContent[] = list.toArray();

    for (int i = list.size(); i > 1; i--) {
        swap(newContent, i - 1, rnd.nextInt(i));
    }

    list.setAll(newContent); 
}

来源:javafx.collections.fxcollections,javafx 15。
如您所见,元素被提取到一个数组中,数组被洗牌,然后列表中的元素被数组替换。这就导致了一个“替代变化”被炒鱿鱼。

相关问题