Java Hashmap 到 JavaFX Treeview 并返回?

Java Hashmap to JavaFX Treeview and Back?

我有一个嵌套的 HashMap,看起来像这样:

Map<String,Object> myMap = new HashMap<String,Object>();

这个myMap是嵌套的,喜欢:

String key => String val
String key => String val
String key => Map<String,Object> val

然后该值可能包含另一个类似的 Map<String,Object>。 我不希望它嵌套超过 3 层。 叶子或最后一个值总是 String.

现在我正在尝试找到一种在 JavaFX GUI 中编辑此 HashMap 的方法。

据我目前所学, 最好的方法似乎是制作可编辑的 JavaFX TreeView 并以某种方式将 Map 翻译成 TreeView 并返回。

到目前为止我在想

TreeMap<String, Object> treeMap = new TreeMap<String, Object>();
treeMap.putAll(myMap); 

然后以某种方式将 TreeMap 转换为 JavaFX TreeView。 但我无法弄清楚如何进行。 另一个令人头疼的问题是,在用户编辑之后,我需要将其全部翻译回 HashMap,例如原始的 myMap。虽然不需要排序/顺序。

没有可以转换的内置方法:

  1. 要么 Map 直接到 TreeView
  2. TreeMapTreeView

此外,如果你看Map<?,?>的结构,我们可以看到它是key, value对的组合,而TreeView<?>只包含值,就像Collection 界面。所以不可能在 TreeView 中插入 key, value 对。但是,您只能插入来自 Map.

的值

你能做的最好的事情就是像这样定义一个新的数据结构:

public class YourCustomDataStructure extends TreeItem<String> {
    ...

    /* 
    Now you can define methods that will convert `List`s directly to `YourCustomDataStructure`. 
    You can even define method to convert `Map` values to `YourCustomDataStructure`.
    */

    public boolean addAll(Map map) {
        //your implementation to convert map to TreeItem
    }

    public boolean addAll(List list) {
        //your implementation to convert list to TreeItem
    }


}

现在使用上一步的 addAll() 方法将 listmap 转换为 YourCustomDataStructure

List<Object> list = getListFromSomewhere();

YourCustomDataStructure<String> customList = new YourCustomDataStructure<String>("customList Node");
customList.getChildren().addAll(list);

现在由于 YourCustomDataStructure 扩展了 TreeItem 所以它的对象可以直接传递给 TreeView 的构造函数,它们将自动转换为 TreeView.

TreeView<String> treeView = new TreeView<String>(customList);

P.S.: 我知道定义新的数据结构和所有的方法在初始阶段需要很多努力,但是一旦定义了这些方法,就变得太容易了将 TreeView 转换为 Map,反之亦然。

创建一个合适的 class 来表示地图条目。由于您需要修改 Map,因此您需要在 class.

中存储密钥和 Map

如果您使用 TextFieldTreeCell 作为单元格类型,StringConverter 可用于在编辑时修改源数据结构:

private static TreeItem<MapItem> createTree(Map<String, Object> map) {
    TreeItem<MapItem> result = new TreeItem<>();

    for (Map.Entry<String, Object> entry : map.entrySet()) {
        result.getChildren().add(createTree(map, entry));
    }

    return result;
}

private static TreeItem<MapItem> createTree(Map<String, Object> map, Map.Entry<String, Object> entry) {
    MapItem mi = new MapItem(map, entry.getKey());
    TreeItem<MapItem> result = new TreeItem<>(mi);

    Object value = entry.getValue();

    if (value instanceof Map) {
        Map<String, Object> vMap = (Map<String, Object>)value;

        // recursive creation of subtrees for map entries
        for (Map.Entry<String, Object> e : vMap.entrySet()) {
            result.getChildren().add(createTree(vMap, e));
        }
    } else {
        result.getChildren().add(new TreeItem<>(new MapItem(null, value.toString())));
    }

    return result;
}

private static class MapItem {

    private final Map<String, Object> map;
    private final String value;

    public MapItem(Map<String, Object> map, String value) {
        this.map = map;
        this.value = value;
    }
}

private static class Converter extends StringConverter<MapItem> {

    private final TreeCell<MapItem> cell;

    public Converter(TreeCell<MapItem> cell) {
        this.cell = cell;
    }

    @Override
    public String toString(MapItem object) {
        return object == null ? null : object.value;
    }

    @Override
    public MapItem fromString(String string) {
        MapItem mi = cell.getItem();

        if (mi != null) {
            TreeItem<MapItem> item = cell.getTreeItem();
            if (item.isLeaf()) {
                MapItem parentItem = item.getParent().getValue();

                // modify value in parent map
                parentItem.map.put(parentItem.value, string);
                mi = new MapItem(mi.map, string);
            } else if (!mi.map.containsKey(string)) {
                // change key of mapping, if there is no mapping for the new key
                mi.map.put(string, mi.map.remove(mi.value));
                mi = new MapItem(mi.map, string);
            }
        }

        return mi;
    }

}

@Override
public void start(Stage primaryStage) {
    Map<String, Object> map = new HashMap<>();

    map.put("a", "b");

    Map<String, Object> inner = new HashMap<>();
    map.put("c", inner);
    inner.put("d", "e");

    Map<String, Object> inner2 = new HashMap<>();
    inner.put("f", inner2);
    inner2.put("g", "h");
    inner2.put("i", "j");

    TreeView<MapItem> treeView = new TreeView<>(createTree(map));
    treeView.setEditable(true);
    treeView.setShowRoot(false);

    treeView.setCellFactory(t -> {
        TextFieldTreeCell<MapItem> cell = new TextFieldTreeCell<>();
        cell.setConverter(new Converter(cell));
        return cell;
    });

    Button btn = new Button("Print Map");
    btn.setOnAction((ActionEvent event) -> {
        System.out.println(map);
    });

    VBox root = new VBox(10, btn, treeView);

    Scene scene = new Scene(root);

    primaryStage.setScene(scene);
    primaryStage.show();
}