【问题标题】:How to Flatten a HashMap?如何展平 HashMap?
【发布时间】:2025-12-03 06:25:02
【问题描述】:

我有一个嵌套的HashMap 这种形式:

{key1=val1, key2=val2, 
    key3=[
            {key4=val4, key5=val5}, 
            {key6=val6, key7=val7} 
        ]
}

我现在想要展平该地图,以便所有条目都处于同一级别:

{key1=val1, key2=val2, key4=val4, key5=val5,key6=val6, key7=val7}

当我尝试时

map.values().forEach(map.get("key3")::addAll);

作为described in this post,我收到以下错误:

invalid method reference
  cannot find symbol
    symbol:   method addAll(T)
    location: class Object
  where T is a type-variable:
    T extends Object declared in interface Iterable

是否有任何通用方法来展平任何给定的Map

【问题讨论】:

  • 一个带有instanceOf 检查Map 的递归函数,我假设
  • 你可以尝试使用 Java Stream API 中的 flatMap()
  • map.get("key3")::addAll 没有意义,因为您似乎有一个Map<String, Object>
  • 如果您能分享您所分享的代码工作的Map 的类型,我将非常高兴。
  • 那么你的Map是什么类型的?如果某些条目是Entry<String, Map<String, String>>,则不能是Map<String, String>。您需要包含代码以便我们了解您实际拥有的内容。

标签: java dictionary hash java-8 flatten


【解决方案1】:

你应该试试这个:

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

    map.forEach((key, value) -> {
      if(value instanceof Map) {
        flatenedMap.putAll((Map) value);
      } else {
        flatenedMap.put(key, value);
      }
    });

如果你有不止一层的嵌套,你可以使用递归算法。

static Map<String, Object> flatMap(Map<String, Object> map) {
    Map<String, Object> flatenedMap = new HashMap<>();
    map.forEach((key, value) -> {
      if(value instanceof Map) {
        flatenedMap.putAll(flatMap((Map) value));
      } else {
        flatenedMap.put(key, value);
      }
    });

    return flatenedMap;
  }

【讨论】:

  • 如果嵌套的地图层不止 2 个,你会怎么做?
  • 他写道他已经嵌套了HashMap,如果他有不止一层嵌套,那么最好是使用一些递归算法
  • @Lino 确实,我的错。
【解决方案2】:

不确定我是否正确理解了这个问题,但这样的事情可能会奏效。 还没有检查所有的语法,所以可能有些地方有错误。

Stream<Map.Entry<String, String>> flatten(Map<String, Object> map) {
  return map.entrySet()
            .stream()
            .flatMap(this::extractValue);         
}

Stream<Map.Entry<String, String>> extractValue(Map.Entry<String, Object> entry) {
   if (entry.getValue() instanceof String) {
      return Stream.of(new AbstractMap.SimpleEntry(entry.getKey(), (String) entry.getValue()));
   } else if (entry.getValue() instanceof Map) {
      return flatten((Map<String, Object>) entry.getValue());
   }
}

那么你可以这样做:

Map<String, String> flattenedMap = flatten(yourmap)
   .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

【讨论】:

  • extractValue 方法无法编译,return 会丢失,以防ifs 都失败
  • @Chirlo 是的,因为我真的不知道 OP 在这种情况下想要做什么
【解决方案3】:

您可以使用递归辅助方法:

static void forEachValue(Map<String, Object> source, BiConsumer<? super String, ? super Object> action) {
    for (final Map.Entry<String, Object> entry : source.entrySet()) {
        if (entry.getValue() instanceof Map) {
            forEachValue((Map<String, Object>) entry.getValue(), action);
        } else {
            action.accept(entry.getKey(), entry.getValue());
        }
    }
}

然后可以这样调用:

Map<String, Object> map = ...;

Map<String, Object> flattened = new HashMap<>();
forEachValue(map, map::put);

我已将这种方法与BiConsumer 一起使用,以不限制该方法仅将嵌套地图展平为另一个地图,但调用者可以自己决定他想对每个键值对做什么。

【讨论】: