Balage1551
Balage1551

Reputation: 1067

JavaFX: Selecting a lot of rows in a large table

It seems a simple question, but I can't find a solution myself.

I have a TableView (to be frank I have two TableViews and would like to put the items from one to the other) with a large number of rows (for my tests, I had a little more than 6600) in it.

When I move the items, I would like to modify the selection of the destination table to be exactly the ones freshly selected.

@FXML
private TableView<PositionWrapper> source;

@FXML
private TableView<PositionWrapper> target;

private ObservableList<PositionWrapper> sourceList = FXCollections.observableArrayList();

private ObservableList<PositionWrapper> targetList = FXCollections.observableArrayList();


private void moveSourceToTarget(MouseEvent event) {
    // collecting selected items
    ObservableList<PositionWrapper> selectedItems = 
        FXCollections.observableArrayList(
            source.getSelectionModel().getSelectedItems());

    // Adding selected items to the target
    targetList.addAll(selectedItems);

    // Removing selected items from source
    sourceList.removeAll(selectedItems);

    // Clearing the selection in both table (source has none of the 
    // selected items now, the target selection should be updated completely)
    target.getSelectionModel().clearSelection();
    source.getSelectionModel().clearSelection();

    // My first try. Doesn't work (UnsupportedOperationException)
    target.getSelectionModel().getSelectedItems().setAll(selectedItems);

    // Second try: doing it one-by-one. Works, but extremely slow.
    selectedItems.stream().forEach(p -> target.getSelectionModel().select(p));
}

The second alternative works, but extremely slow. (How slow I don't know, because I killed my program after 20 seconds.)

My question: how to select a large number of rows in a TableView quickly?

Upvotes: 0

Views: 634

Answers (1)

Balage1551
Balage1551

Reputation: 1067

I made some deeper search and checked the implementation source code of the MultipleSelectionModelBase class. When selecting an item by row content (as I did in my second try), it iterates over all the items in the table to find the index of it, then calls the select(int) method with the index found.

It means that selecting several rows by content is an n*m algorithm, with a worst case of n^2 when selecting (almost) all items.

But this gave me the solution as well. As it is now proved to me that only the by-value selection suffers from the n^2 anomaly, the natural workaround is to use the index-based selection.

Here is my code snippet:

private void moveSourceToTarget(MouseEvent event) {
    [...]

    // Allocating an array to store indexes
    int[] idx = new int[selectedItems.size()];
    int p = 0;
    // Performance tuning to move selected items into a set 
    // (faster contains calls)
    Set<PositionWrapper> s = new HashSet<>(Arrays.asList(
        selectedItems.toArray(new PositionWrapper[0])));

    // Iterating over items in target list (but only once!)
    for (int i = 0; i < target.getItems().size(); i++) {
        if (s.contains(target.getItems().get(i))) {
            // and adding to the list of indexes when selected
            idx[p++] = i;
        }
    }
    // Calling the more effective index-based selection setter
    target.getSelectionModel().selectIndices(-1, idx);
}

Aftermath: the time required for selecting the 6600+ items reduced from 20+ sec to 0.1 sec.

BTW, I don't know why isn't selectAll(S...) is part of the JavaFX code...

Upvotes: 1

Related Questions