我想要展平一个HashMap实例,如本例所示。注意,数据不是JSON格式的,这只是一个伪代码:
nested = {
"one": {
"two": {
"2a": "x",
"2b": "y"
}
},
"side": "value"
}
// output: { "one.two.2a": "x", "one.two.2b": "y", "side": "value" }不幸的是,我找不到任何参考实现,所以我想出了我的递归解决方案,如下所示。有没有更好的方法(不使用递归、不使用性能、不使用安全性或不使用清洁性:)来实现这一点?输出应该是另一个展平形式的HashMap。
我将把结果用于这种目的https://redislabs.com/redis-best-practices/data-storage-patterns/object-hash-storage/
public class Flat {
public static void flatten(Map<String, ?> target, Map<String, String> result, String path) {
for (var entry : target.entrySet()) {
var next = path.equals("") ? entry.getKey() : path + "." + entry.getKey();
if (entry.getValue() instanceof Map) {
flatten((Map) entry.getValue(), result, next);
} else {
result.put(next, entry.getValue().toString());
}
}
}
public static Map unflatten(Map<String, String> target) {
var result = new HashMap<String, Object>();
for (var entry : target.entrySet()) {
if (entry.getKey().split(".").length == 1) {
result.put(entry.getKey(), entry.getValue());
} else {
var path = entry.getKey().split(".");
Map<String, Object> current = new HashMap<>();
for (var i = 0; i < path.length - 1; i++) {
if (result.containsKey(path[i])) {
current = (Map) (result.get(path[i]));
} else {
current = new HashMap<>();
result.put(path[i], current);
}
}
current.put(path[path.length - 1], entry.getValue());
}
}
return result;
}
}发布于 2020-05-18 18:08:34
如果你想清理递归代码,你可以像下面这样更新它:
public static Map<String, String> flatten(Map<String, ?> source) {
Map<String, String> converted = new HashMap<>();
for (var entry : source.entrySet()) {
if (entry.getValue() instanceof Map) {
flatten((Map<String, Object>) entry.getValue())
.forEach((key, value) -> converted.put(entry.getKey() + "." + key, value));
} else {
converted.put(entry.getKey(), entry.getValue().toString());
}
}
return converted;
}多亏了一条评论,我也研究了堆栈解决方案。您可以使用下面的示例重写展平。您应该使用哪个版本取决于开发人员的技能水平,因为堆叠版本有点难以理解。
private static class StackElement {
Optional<String> key;
Map<String, ?> elements;
public StackElement(String key, Map<String, ?> elements) {
this.key = Optional.ofNullable(key);
this.elements = elements;
}
}
public static Map<String, String> flattenNonRecursive(Map<String, ?> source) {
Map<String, String> converted = new HashMap<>();
Stack<StackElement> stack = new Stack();
stack.push(new StackElement(null, source));
while (!stack.empty()) {
var frame = stack.pop();
for (var entry : frame.elements.entrySet()) {
var frameKey = frame.key
.map(k -> k + ".")
.orElse("") + entry.getKey();
if (entry.getValue() instanceof Map) {
stack.push(new StackElement(frameKey, (Map<String, ?>) entry.getValue()));
} else {
converted.put(frameKey, entry.getValue().toString());
}
}
}
return converted;
}在性能方面,非递归的速度更快。我用Map.of("sample.test.two", "one", "test.sample.two", "three", "four", "file")的地图做了一个小实验。
调用该方法1000倍的性能差异是:
Recursive took: 20957300
Non recursive took: 13376000至于你的unflatten,这里面包含了bug。在一次测试中,我使用了一个只包含两个元素的简单map,它因索引越界而崩溃。这与您在错误的地方使用result和current有关。以下是略微更改的工作副本:
public static Map<String, ?> unflatten(Map<String, String> target) {
var result = new HashMap<String, Object>();
for (var entry : target.entrySet()) {
var split = entry.getKey().split("\\.");
if (split.length == 1) {
result.put(entry.getKey(), entry.getValue());
continue;
}
var current = result;
for (int i = 0; i < split.length - 1; i++) {
current = (HashMap<String, Object>) current.computeIfAbsent(
split[i], p -> new HashMap<String, Object>());
}
current.put(split[split.length - 1], entry.getValue());
}
return result;
}https://stackoverflow.com/questions/61865457
复制相似问题