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
。虽然不需要排序/顺序。
没有可以转换的内置方法:
- 要么
Map
直接到 TreeView
- 或
TreeMap
到TreeView
此外,如果你看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()
方法将 list
或 map
转换为 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();
}
我有一个嵌套的 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
。虽然不需要排序/顺序。
没有可以转换的内置方法:
- 要么
Map
直接到TreeView
- 或
TreeMap
到TreeView
此外,如果你看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()
方法将 list
或 map
转换为 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();
}