FilteredList 在更新时给出 java.lang.ArrayIndexOutOfBoundsException

FilteredList gives java.lang.ArrayIndexOutOfBoundsException on update

我创建了一个简单的应用程序来测试筛选列表及其在相应源列表更改时的行为。我也想测试 update 更改,所以我创建了 ObservableListObservableList。它比创建额外的 class 具有可观察字段的 Person 更快更简单。

代码看起来是这样的:

    ListChangeListener<ObservableList<String>> changeNotifier = new ListChangeListener<ObservableList<String>>() {
        @Override
        public void onChanged(Change<? extends ObservableList<String>> c) {
            while (c.next()) {
                if (c.wasPermutated()) {
                    System.out.println("permutation");
                } else if (c.wasUpdated()) {
                    System.out.println("update");
                } else {
                    if (c.wasRemoved()) {
                        System.out.println("remove");
                    }
                    if (c.wasAdded()) {
                        System.out.println("add");
                    }
                    if (c.wasReplaced()) {
                        System.out.println("replace");
                    }
                }
            }
        }
    };
    Callback<ObservableList<String>, Observable[]> identityExtractor = new Callback<ObservableList<String>, Observable[]>() {
        @Override
        public Observable[] call(ObservableList<String> param) {
            return new Observable[]{param};
        }
    };
    Predicate<ObservableList<String>> nonEmptyFilter = new Predicate<ObservableList<String>>() {
        @Override
        public boolean test(ObservableList<String> obsl) {
            boolean nonEmpty = ! obsl.isEmpty();
            for (String item : obsl) {
                nonEmpty = nonEmpty && (null != item) && ("" != item);
            };
            return nonEmpty;
        }
    };

    ObservableList<ObservableList<String>> basicSimple = FXCollections.observableArrayList();
    ObservableList<ObservableList<String>> basicComposed = FXCollections.observableArrayList( identityExtractor );

    ObservableList<ObservableList<String>> filteredSimple = basicSimple.filtered( nonEmptyFilter );
    ObservableList<ObservableList<String>> filteredComposed = basicComposed.filtered( nonEmptyFilter );

    System.out.println("Basic testing");

    System.out.println("Add invalid");
    basicSimple.addAll( FXCollections.observableArrayList("") );
    System.out.println( basicSimple );
    System.out.println( filteredSimple );

    System.out.println("Make it valid");
    basicSimple.get(0).addAll("first");
    System.out.println( filteredSimple );

    System.out.println("Add valid");
    basicSimple.addAll( FXCollections.observableArrayList("Second") );
    System.out.println( filteredSimple );

    System.out.println("Composed testing");

    System.out.println("Add invalid");
    basicComposed.addAll( FXCollections.observableArrayList("") );
    System.out.println( basicComposed );
    System.out.println( filteredComposed );

    System.out.println("Make it valid");
    basicComposed.get(0).addAll("first");
    System.out.println( filteredComposed );

    System.out.println("Add valid");
    basicComposed.addAll( FXCollections.observableArrayList("Second") );
    System.out.println( filteredComposed );

我在测试过程中发现了一个奇怪的错误:

[info] Running helloworld.HelloWorld 
Basic testing
Add invalid
[[]]
[]
Make it valid
[]
Add valid
[[Second]]
Composed testing
Add invalid
[[]]
[]
Make it valid
[error] (JavaFX Application Thread) java.lang.ArrayIndexOutOfBoundsException
java.lang.ArrayIndexOutOfBoundsException
        at java.lang.System.arraycopy(Native Method)
        at javafx.collections.transformation.FilteredList.updateFilter(FilteredList.java:298)
        at javafx.collections.transformation.FilteredList.update(FilteredList.java:239)
        at javafx.collections.transformation.FilteredList.sourceChanged(FilteredList.java:137)
        at javafx.collections.transformation.TransformationList.lambda$getListener(TransformationList.java:106)
        at javafx.collections.transformation.TransformationList$$Lambda/1596532574.onChanged(Unknown Source)
        at javafx.collections.WeakListChangeListener.onChanged(WeakListChangeListener.java:88)
        at com.sun.javafx.collections.ListListenerHelper$SingleChange.fireValueChangedEvent(ListListenerHelper.java:164)
        at com.sun.javafx.collections.ListListenerHelper.fireValueChangedEvent(ListListenerHelper.java:73)
        at javafx.collections.ObservableListBase.fireChange(ObservableListBase.java:233)
        at javafx.collections.ListChangeBuilder.commit(ListChangeBuilder.java:485)
        at javafx.collections.ListChangeBuilder.endChange(ListChangeBuilder.java:541)
        at javafx.collections.ObservableListBase.endChange(ObservableListBase.java:205)
        at com.sun.javafx.collections.ObservableListWrapper.access0(ObservableListWrapper.java:45)
        at com.sun.javafx.collections.ObservableListWrapper.invalidated(ObservableListWrapper.java:75)
        at com.sun.javafx.collections.ListListenerHelper$SingleInvalidation.fireValueChangedEvent(ListListenerHelper.java:126)
        at com.sun.javafx.collections.ListListenerHelper.fireValueChangedEvent(ListListenerHelper.java:73)
        at javafx.collections.ObservableListBase.fireChange(ObservableListBase.java:233)
        at javafx.collections.ListChangeBuilder.commit(ListChangeBuilder.java:482)
        at javafx.collections.ListChangeBuilder.endChange(ListChangeBuilder.java:541)
        at javafx.collections.ObservableListBase.endChange(ObservableListBase.java:205)
        at javafx.collections.ModifiableObservableListBase.addAll(ModifiableObservableListBase.java:102)
        at javafx.collections.ObservableListBase.addAll(ObservableListBase.java:245)
        at helloworld.HelloWorld.start(HelloWorld.java:87)
        at com.sun.javafx.application.LauncherImpl.lambda$launchApplication13(LauncherImpl.java:821)
        at com.sun.javafx.application.LauncherImpl$$Lambda/7143454.run(Unknown Source)
        at com.sun.javafx.application.PlatformImpl.lambda$runAndWait6(PlatformImpl.java:323)
        at com.sun.javafx.application.PlatformImpl$$Lambda/397137382.run(Unknown Source)
        at com.sun.javafx.application.PlatformImpl.lambda$null4(PlatformImpl.java:292)
        at com.sun.javafx.application.PlatformImpl$$Lambda/1802784360.run(Unknown Source)
        at java.security.AccessController.doPrivileged(Native Method)
        at com.sun.javafx.application.PlatformImpl.lambda$runLater5(PlatformImpl.java:291)
        at com.sun.javafx.application.PlatformImpl$$Lambda/1184782272.run(Unknown Source)
        at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
        at com.sun.glass.ui.gtk.GtkApplication._runLoop(Native Method)
        at com.sun.glass.ui.gtk.GtkApplication.lambda$null(GtkApplication.java:126)
        at com.sun.glass.ui.gtk.GtkApplication$$Lambda/450111611.run(Unknown Source)
        at java.lang.Thread.run(Thread.java:745)
[trace] Stack trace suppressed: run last compile:run for the full output.
[]
Add valid
[[Second]]

basicSimplebasicComposed的区别在于后者定义了一个提取器,因此它接收更新事件。在处理更新事件的过程中,本机代码抛出了异常。我应该考虑什么才能使示例代码正常运行?


一些调试

我已将 println 插入到 nonEmptyFilter 谓词测试的末尾。它工作正常,传递给它的 ObservableList 是预期的刚刚更新的新值。稍后在执行 FilteredList 的某些内部代码时会出现此错误。

所以异常是由 basicComposed.get(0).addAll("first"); 行抛出的,但直观地反驳它不是 get(0) 抛出异常而是 addAll !?

另一件有趣的事情是,如果您删除 identityExtractor 那么异常就会消失?此外,只有在 nonEmptyFilter returns false !

时才会抛出异常

所以 identityExtractornonEmptyFilter 之间存在某种动态导致 addAll 抛出异常。

如果两个以下两个条件成立,抛出异常是可能的:

  1. basicComposed.get(0).isEmpty()
  2. basicComposed.get(0).addAll("") // 添加一个空字符串

这看起来像是一个错误;我认为它可能与 this one 相同,错误报告说它已针对 JavaFX 8u60 修复(对于 8u40 可能已经太晚了)。

我在 1.8 上测试过。0_40-ea-b23??和 1.9.0-ea-b49 并且错误出现在两个版本中。如果当前有 1.8.0u60 的 ea 版本,我不确定在哪里可以找到它。