【发布时间】:2017-10-21 08:13:17
【问题描述】:
我有一个Albums 表,用户可以过滤和排序。
这是这张桌子的样子。如您所见,列是可排序的,顶部有一个文本框,当前正在过滤其中包含字符串“cu”的专辑:
在填充专辑列表后,一切正常。
但是,如果我在填充专辑列表时尝试排序或过滤,我会得到一个ConcurrentModificationException。专辑列表最初需要几分钟来加载大型库,我正在使用 WatchService 自动更新列表以更改文件系统,因此在加载列表时禁用功能不是一个选项。
以下是一些简化的代码片段,说明了如何设置:
设置列表(使用here提出的想法)
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 );
}
设置过滤框和谓词
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 albums 进行所有修改通过这个图书馆线程。
public class Library {
...
private static void addAlbum ( Album album ) {
albums.add ( album );
}
private static void removeAlbum ( Album album ) {
albums.remove ( album );
}
...
}
最后,我尝试了两种不同的方法来解决这个问题:
1.使用 Vector 作为 Observable List 的基础数据结构 - 似乎根本没有任何影响。同样的错误。
2。将 ObservableList albums 的所有修改包装在 Platform.callLater 中。这导致表停止更新:
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 );
}
});
}
...
}
关于如何解决此问题的任何建议?
以下是解决方案的主要特点:
- tableview 始终是可排序的
- 表格视图始终可过滤
- 随着专辑列表的变化,表格视图会定期更新(它不必是即时的,但必须至少每隔几秒更新一次)。
【问题讨论】:
-
你试过 synchronizedObservableList 了吗?
标签: java multithreading javafx thread-safety