Ich habe meine Map Table-Listener in eine Unterklasse von TableView gepackt.
package tablemap;
import java.util.AbstractMap;
import java.util.Map;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.MapChangeListener;
import javafx.collections.ObservableList;
import javafx.collections.ObservableMap;
import javafx.scene.control.TableView;
public class MapTableView<K,V> extends TableView<Map.Entry<K,V>>{
private final ObservableList<Map.Entry<K,V>> obsList;
private final ObservableMap<K,V> map;
private final MapChangeListener<K,V> mapChange;
private final ListChangeListener<Map.Entry<K,V>> listChange;
public MapTableView(ObservableMap<K,V> map) {
this.map = map;
obsList = FXCollections.observableArrayList(map.entrySet());
setItems(obsList);
mapChange = new MapChangeListener<K, V>() {
@Override
public void onChanged(MapChangeListener.Change<? extends K, ? extends V> change) {
obsList.removeListener(listChange);
if (change.wasAdded())
obsList.add(new AbstractMap.SimpleEntry(change.getKey(),change.getValueAdded()));
if (change.wasRemoved()){
//obsList.remove(new AbstractMap.SimpleEntry(change.getKey(),change.getValueRemoved()));
//^doesn't work always, use loop instead
for (Map.Entry<K,V> me : obsList){
if (me.getKey().equals(change.getKey())){
obsList.remove(me);
break;
}
}
}
obsList.addListener(listChange);
}
};
listChange = (ListChangeListener.Change<? extends Map.Entry<K, V>> change) -> {
map.removeListener(mapChange);
while (change.next()){
//maybe check for uniqueness here
if (change.wasAdded()) for (Map.Entry<K, V> me: change.getAddedSubList())
map.put(me.getKey(),me.getValue());
if (change.wasRemoved()) for (Map.Entry<K, V> me: change.getRemoved())
map.remove(me.getKey());
}
map.addListener(mapChange);
};
map.addListener(mapChange);
obsList.addListener(listChange);
}
//adding to list should be unique
public void addUnique(K key, V value){
boolean isFound = false;
//if a duplicate key just change the value
for (Map.Entry<K,V> me : getItems()){
if (me.getKey().equals(key)){
isFound = true;
me.setValue(value);
break;//only first match
}
}
if (!isFound) // add new entry
getItems().add(new AbstractMap.SimpleEntry<>(key,value));
}
//for doing lenghty map operations
public void removeMapListener(){
map.removeListener(mapChange);
}
//for resyncing list to map after many changes
public void resetMapListener(){
obsList.removeListener(listChange);
obsList.clear();
obsList.addAll(map.entrySet());
obsList.addListener(listChange);
map.addListener(mapChange);
}
}
Es scheint so weit zu arbeiten. Ich erstelle mit folgendem Code:
final ObservableMap<String, LineItem> obsMap = FXCollections.observableHashMap();
final MapTableView<String,LineItem> mtv = new MapTableView(obsMap);
Sie können sogar die Schlüssel bearbeiten.
final TableColumn<Map.Entry<String,LineItem>,String> keyCol = new TableColumn("Key");
keyCol.setCellValueFactory(
(TableColumn.CellDataFeatures<Map.Entry<String,LineItem>, String> p) ->
new SimpleStringProperty(p.getValue().getKey()));
keyCol.setCellFactory(TextFieldTableCell.forTableColumn());
keyCol.setOnEditCommit((CellEditEvent<Map.Entry<String,LineItem>, String> t) -> {
final String oldKey = t.getOldValue();
final LineItem oldLineItem = obsMap.get(oldKey);
obsMap.remove(oldKey);//should remove from list but maybe doesn't always
obsMap.put(t.getNewValue(),oldLineItem);
});
Sie können sehen, ich habe eine Methode hinzugefügt, um die Kartenlistener zu entfernen und neu hinzuzufügen. Das Hinzufügen und Entfernen von 100k-Einträgen dauert 0,65 Sekunden ohne Hörer und 5,2 Sekunden mit ihnen.
Hier ist das Ganze in einer Datei auf Pastebin. http://pastebin.com/NmdTURFt
Das wäre so viel schöner, wenn ObservableMap # entrySet kovariantly überladen wäre, um ein ObservableSet anstelle eines regulären Satzes zurückzugeben. - obwohl dies nicht wahr ist, weil eine Tabellenansicht eine beobachtbare Liste benötigt, und es gibt keinen einfachen Weg, von einer beobachtbaren Menge zu einer beobachtbaren Liste zu gelangen. – Groostav