我目前有一个格式为
的键值对映射a.b.c: value1
e.f: value2
g: [
g.h: nested_value1
g.i: nested_value2
]
我需要在平面结构中“取消”这个新地图 -
a:
b:
c: value1
e:
f: value2
g: [
h: nested_value1
i: nested_value2
]
我当前的尝试没有走得太远,并抛出ConcurrentModificationException
private static Map<String, Object> unflatten(Map<String, Object> flattened) {
Map<String, Object> unflattened = new HashMap<>();
for (String key : flattened.keySet()) {
doUnflatten(flattened, unflattened, key, flattened.get(key));
}
return unflattened;
}
private static Map<String, Object> doUnflatten(
Map<String, Object> flattened,
Map<String, Object> unflattened,
String key,
Object value) {
String[] parts = StringUtils.split(key, '.');
for (int i = 0; i < parts.length; i++) {
String part = parts[i];
Object current = flattened.get(part);
if (i == (parts.length - 1)) {
unflattened.put(part, value);
} else if (current == null) {
if ((current = unflattened.get(part)) == null) {
current = new HashMap<>();
}
unflattened.put(part, current);
unflattened = (Map<String, Object>) current;
} else if (current instanceof Map) {
unflattened.put(part, current);
unflattened = (Map<String, Object>) current;
}
}
return unflattened;
}
我错过了一些明显的东西吗?一种解决方案是使用像JsonFlattener这样的库 - 唯一的问题是这将涉及在JSON之间进行前后转换。
编辑:感谢指点 - 我在那里,有一件事我忘了提到它还需要解决HashMaps的集合
答案 0 :(得分:1)
您的错误来自于您迭代密钥集然后更改地图,而不是通过迭代器。
所有这类&#34;集合视图返回的迭代器 方法&#34;快速失败:如果地图在任何地方进行了结构修改 创建迭代器之后的时间,除了通过之外的任何方式 迭代器自己的remove方法,迭代器会抛出一个 ConcurrentModificationException的。因此,面对并发 修改,迭代器快速而干净地失败,而不是 在不确定的时间冒着任意的,非确定性的行为 在将来。
您可以使用新地图来解决这个问题。
答案 1 :(得分:1)
您的实现问题在于您将输出写入用于输入的同一Map
,这会导致ConcurrentModificationException
。
对于输出单独的Map
,实现变得简单:
Map<String,Object> unflattened = new HashMap<>();
for (Map.Entry<String,Object> e : flattened.entrySet()) {
String[] parts = StringUtils.split(e.getKey(), ".");
// Find the map to be used as a destination for put(...)
Map<String,Object> dest = unflattened;
for (int i = 0 ; i != parts.length-1 ; i++) {
Object tmp = dest.get(parts[i]);
if (tmp == null) {
// We did not see this branch yet
Map<String,Object> next = new HashMap<>();
dest.put(parts[i], next);
dest = next;
continue;
}
if (!(temp instanceof Map)) {
throw new IllegalStateException();
}
dest = (Map<String,Object>)temp;
}
// Put the entry into the destination Map<>
dest.put(parts[parts.length-1], e.getValue());
}
请注意,当初始地图描述不一致的层次结构时,“unflattening”的过程可能会失败,例如,具有分支和具有相同名称的叶子的层次结构:
"a.b.c" -> "x" // OK: "a.b" is a branch
"a.b.d" -> "y" // OK: "a.b" is a branch
"a.b" -> "z" // Error: "a.b" is a leaf
答案 2 :(得分:1)
为您的结果创建一个新的Map
实例,而不是尝试重用当前的实例。另外,发送地图值,因此不需要提取:
private static Map<String, Object> unflatten(Map<String, Object> flattened) {
Map<String, Object> unflattened = new HashMap<>();
for (String key : flattened.keySet()) {
doUnflatten(unflattened, key, flattened.get(key));
}
return unflattened;
}
这也可以防止原始键出现在生成的地图中。
以上还需要略微重写doUnflatten
方法:
private static void doUnflatten(Map<String, Object> current, String key,
Object originalValue) {
String[] parts = StringUtils.split(key, ".");
for (int i = 0; i < parts.length; i++) {
String part = parts[i];
if (i == (parts.length - 1)) {
current.put(part, originalValue);
return;
}
Map<String, Object> nestedMap = (Map<String, Object>) current.get(part);
if (nestedMap == null) {
nestedMap = new HashMap<>();
current.put(part, nestedMap);
}
current = nestedMap;
}
}
注意事项:没有必要从方法中返回地图。将循环划分为两种不同的情况:应将值写入映射,或者应创建或检索嵌套映射。
答案 3 :(得分:0)
最简单的解决方案是替换行
for (String key : flattened.keySet()) {
到
for (String key : new ArrayList<>(flattened.keySet())) {
但是对于大数据量,从性能角度来看,它可能不是很有效。