JavaFX ObservableList - 添加项会导致ConcurrentModificationException [英] JavaFX ObservableList - Adding Items causes ConcurrentModificationException

查看:165
本文介绍了JavaFX ObservableList - 添加项会导致ConcurrentModificationException的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一张专辑的表格,可由用户过滤和排序。

I have a table of Albums that is filterable and sortable by the user.

这是什么表看起来像。如您所见,列是可排序的,顶部有一个文本框,当前正在过滤其中包含字符串cu的专辑:

Here is what that table looks like. As you can see, the columns are sortable, and there is a text box at the top, which is currently filtering for albums with the string "cu" in them:

填充完相册列表后,所有内容都完美无缺

Everything works perfectly after the list of albums is populated.

但是,如果我在填充相册列表时尝试排序或过滤,我会得到 ConcurrentModificationException 。专辑列表最初需要几分钟才能加载大型库,我使用WatchService自动更新列表并更改文件系统,因此在加载列表时禁用功能不是一种选择。

However, if I try to sort or filter while the album list is populating, I get a ConcurrentModificationException. The album list initially takes a few minutes to load for a large library, and I am using a WatchService to automatically update the list with changes to the file system, so disabling functionality while the list is loading isn't an option.

以下是一些简化的代码snipets,说明了如何设置:

Here are some simplified code snipets which illustrate how things are setup:

设置列表(使用此处)提出的想法

public class Library {
    final static ObservableList <Album> albums = FXCollections.observableArrayList( new ArrayList <Album>() );
    final static FilteredList <Album> albumsFiltered = new FilteredList <Album>( albums, p -> true );
    final static SortedList <Album> albumsSorted = new SortedList <Album>( albumsFiltered );
}

构建表格

public void setupAlbumTable () {
    TableColumn artistColumn = new TableColumn( "Artist" );
    TableColumn yearColumn = new TableColumn( "Year" );
    TableColumn albumColumn = new TableColumn( "Album" );

    artistColumn.setCellValueFactory( new PropertyValueFactory <Album, String>( "albumArtist" ) );
    yearColumn.setCellValueFactory( new PropertyValueFactory <Album, Integer>( "year" ) );
    albumColumn.setCellValueFactory( new PropertyValueFactory <Album, String>( "title" ) );

    albumTable = new TableView();
    albumTable.getColumns().addAll( artistColumn, yearColumn, albumColumn );
    albumTable.setItems( Library.albumsSorted );

    Library.albumsSorted.comparatorProperty().bind( albumTable.comparatorProperty() );

    albumTable.getSortOrder().add( artistColumn );
    albumTable.getSortOrder().add( yearColumn );
    albumTable.getSortOrder().add( albumColumn );
}

设置FilterBox和Predicate

public void setupAlbumFilterPane () {
    TextField filterBox = new TextField();
    filterBox.textProperty().addListener( ( observable, oldValue, newValue ) -> {
        Library.albumsFiltered.setPredicate( album -> {
            if ( newValue == null || newValue.isEmpty() ) {
                return true;
            }

            String[] filterTokens = newValue.toLowerCase().split( "\\s+" );

            ArrayList <String> albumMatchText = new ArrayList <String>();

            albumMatchText.add( album.getAlbumArtist().toLowerCase() );
            albumMatchText.add( album.getTitle().toLowerCase() );
            albumMatchText.add( album.getYear().toLowerCase() );

            for ( String filterToken : filterTokens ) {
                boolean fitlerTokenHasMatch = false;
                for ( String albumInfoString : albumMatchText ) {
                    if ( albumInfoString.contains( filterToken ) ) {
                        fitlerTokenHasMatch = true;
                    }
                }

                if ( !fitlerTokenHasMatch ) {
                    return false;
                }
            }

            return true;
        });
    });
}

添加数据或从列表中删除数据:保证只能从一个线程(不同于JavaFX)调用这些函数,并且只对这一个库线程进行对ObservableList 相册的所有修改。

Adding data or removing data from the lists: These functions are guaranteed to be called only from one Thread (different than the JavaFX), and all modifications to the ObservableList albums are only ever made by this one Library thread.

public class Library {
    ...
    private static void addAlbum ( Album album ) {
        albums.add ( album );
    }

    private static void removeAlbum ( Album album ) {
        albums.remove ( album );
    }
    ...
}

最后,我有尝试了两种不同的方法来解决这个问题:

Finally, I have tried two different approaches to solve this problem:

1。使用Vector作为Observable List的基础数据结构 - 似乎根本没有任何影响。相同的错误。

1. Use a Vector as the underlying data structure for the Observable List - Did not seem to have any impact at all. Same errors.

2。在Platform.callLater 中包装ObservableList 专辑的所有修改。这导致表停止更新:

2. Wrap all modifications of the ObservableList albums in Platform.callLater. This caused the table to stop updating:

public class Library {
    ...
    private static void addAlbum ( Album album ) {
        Platform.runLater( new Runnable() {
            public void run() {
                albums.add ( album );
            }
        });
    }

    private static void removeAlbum ( Album album ) {
        Platform.runLater( new Runnable() {
            public void run() {
                albums.remove ( album );
            }
        });
    }
    ...
}

有关如何的任何建议解决这个问题?

Any suggestions on how to solve this issue?

以下是解决方案的主要特点:

Here are the key features of a solution:


  • tableview是可排序的始终

  • 桌面视图始终可过滤

  • 随着专辑列表的变化,桌面视图会定期更新(不必立即更新) ,但必须至少每隔几秒钟一次。

推荐答案

听起来你正在添加一个项目。只是一次添加所有项目不是一个选项?

It sounds like you're adding one item after the other. Just adding all the items at once is not an option?

这篇关于JavaFX ObservableList - 添加项会导致ConcurrentModificationException的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆