问题
I have a table of Albums
that is filterable and sortable by the user.
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.
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.
Here are some simplified code snipets which illustrate how things are setup:
Setting up the Lists (Using the idea presented 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 );
}
Building the Table
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 );
}
Setting up the FilterBox and 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;
});
});
}
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. Use a Vector as the underlying data structure for the Observable List - Did not seem to have any impact at all. Same errors.
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:
- The tableview is sortable at all times
- The tableview is filterable at all times
- The tableview is updated regularly as the album list changes (it does not have to be instantaneous, but it has to be, at a minimum, every few seconds).
回答1:
It sounds like you're adding one item after the other. Just adding all the items at once is not an option?
来源:https://stackoverflow.com/questions/44099841/javafx-observablelist-adding-items-causes-concurrentmodificationexception