Sarah Szabo
Sarah Szabo

Reputation: 10815

Suspicious Behavior From ListView.getItems().removeAll()

If I have a list containing 4 objects(not all the same), and I call JavaFX'slistView.getItems().removeAll(listView.getSelectionModel().getSelectedItems()) with a single object (let's say number 1) selected, objects 2 and 3 will be removed as well. Is this the normal operation for removeAll()? It led to a rather frustrating bug. This was not documented in the removeAll() method, and is rather misleading because I used it since it has better type-safety than the normal remove method, which takes an Object.

Upvotes: 1

Views: 262

Answers (1)

Stuart Marks
Stuart Marks

Reputation: 132400

When you remove a selected item, the selection will be updated so that some other item will be selected. When the selection is updated, this has the side effect of updating the list returned by listView.getSelectionModel().getSelectedItems(). This interacts badly with the List.removeAll method.

Suppose we have targetList.removeAll(removeList). What happens is that for each element in targetList, the code asks, "does this element appear in removeList?" If it does, the element is removed from targetList. This has a side effect on removeList, which changes the behavior the next time around the loop.

More specifically:

  1. This starts off with element 0. Since this element doesn't appear in removeList, nothing happens.

  2. Next is element 1. Since it's selected, it appears in removeList, so element 1 is removed. The ListView code detects that the selected element has been removed, so it updates the selection to element 2. This means that removeList now contains element 2.

  3. Next is element 2. Since it's selected, it appears in removeList, so element 2 is removed. The ListView code detects that the selected element has been removed, so it updates the selection to element 3. This means that removeList now contains element 3.

  4. I think you can see what's happening now. :-)

Probably the easiest way around this is to copy the selection list before passing it to removeAll:

list.getItems().removeAll(new ArrayList<>(list.getSelectionModel().getSelectedItems()));

Upvotes: 3

Related Questions