首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >展平和取消展平HashMap的最佳方法

展平和取消展平HashMap的最佳方法
EN

Stack Overflow用户
提问于 2020-05-18 16:28:03
回答 1查看 422关注 0票数 5

我想要展平一个HashMap实例,如本例所示。注意,数据不是JSON格式的,这只是一个伪代码:

代码语言:javascript
复制
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/

代码语言:javascript
复制
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;
  }
}
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2020-05-18 18:08:34

如果你想清理递归代码,你可以像下面这样更新它:

代码语言:javascript
复制
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;
}

多亏了一条评论,我也研究了堆栈解决方案。您可以使用下面的示例重写展平。您应该使用哪个版本取决于开发人员的技能水平,因为堆叠版本有点难以理解。

代码语言:javascript
复制
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倍的性能差异是:

代码语言:javascript
复制
Recursive took:         20957300
Non recursive took:     13376000

至于你的unflatten,这里面包含了bug。在一次测试中,我使用了一个只包含两个元素的简单map,它因索引越界而崩溃。这与您在错误的地方使用resultcurrent有关。以下是略微更改的工作副本:

代码语言:javascript
复制
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;
}
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/61865457

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档