JavaFX ObservableList - Adding Items causes ConcurrentModificationException

允我心安 提交于 2019-12-22 17:40:44

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!