Reputation: 1067
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
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