【问题标题】:Java map.get(key) - automatically do put(key) and return if key doesn't exist?Java map.get(key) - 如果 key 不存在,自动执行 put(key) 并返回?
【发布时间】:2012-01-10 04:01:59
【问题描述】:

我厌倦了以下模式:

value = map.get(key);
if (value == null) {
    value = new Object();
    map.put(key, value);
}

此示例仅涉及在您使用嵌套映射表示多维结构时要编写的额外代码的表面。

我确信某处存在避免这种情况,但我的谷歌搜索没有产生任何相关的结果。有什么建议吗?

【问题讨论】:

  • 出于好奇,您要放置的Object,它只是一个Object,还是类型会有所不同?另外,它是已经创建了还是应该只在没有对象存在的情况下才创建?
  • 类型在编译时是已知的。通常它是一个 String to Map (to Map)* to Integer。

标签: java dictionary get null put


【解决方案1】:

java.util.concurrent.ConcurrentMap 

从 Java 8 开始

Java.util.Map

putIfAbsent(K key, V value) 

返回映射到键的值或插入给定值,如果没有为键映射值,则插入 null。

如果您需要对值进行惰性评估,则有

computeIfAbsent(K key, Function<? super K,? extends V> mappingFunction)

【讨论】:

  • 返回:与指定键关联的前一个值,如果该键没有映射,则返回 null
  • 请注意,这会强制您创建一个新对象,即使地图中已经存在一个对象。如果你真的只创建一个new Object(),这可能可以忽略不计。
  • @user802421 是对的——如果插入了值,这将返回 null
  • java.util.Map 在 JDK 8 中已经 putIfAbsent,而不仅仅是 ConcurrentMap。
  • 它不起作用是值本身应该是一个集合。因为我想要的是添加一个空集合并返回它,以便我可以将项目添加到该集合中。
【解决方案2】:

Java 8 为 Map 添加了不错的方法:computecomputeIfPresentcomputeIfAbsent

实现您的需要:

Object existingOrCreated = map.computeIfAbsent(key, (k) -> new Object());

【讨论】:

  • 接受的答案指向同样的方法。但是,这个答案更清楚了。
【解决方案3】:

这种模式的问题在于,您必须以某种方式定义应该使用的值,以防get() 返回 null。

那里当然有图书馆,IIRC 也有一些更新的馆藏可以做到这一点,但不幸的是我不记得那些是。

但是,您可以自己编写实用程序方法,前提是您有创建新值的标准方法。这样的事情可能会起作用:

public static <K, V> V safeGet(K key, Map<K,V> map, Class<V> valueClass) throws /*add exceptions*/ {
  V value = map.get(key);
  if( value == null ) {
    value = valueClass.newInstance();
    map.put( key, value );
  }   

  return value;
} 

请注意,您要么必须抛出反射异常,要么在方法中处理它们。此外,这需要valueClass 提供无参数构造函数。或者,您可以简单地传递应该使用的默认值。

Java 8 更新

其他答案中已经提到过,但为了完整起见,我也会在此处添加信息。

从 Java 8 开始,默认方法 computeIfAbsent(key, mappingFunction) 基本相同,例如如果值类是BigDecimal,它可能看起来像这样:

BigDecimal value = map.computeIfAbsent(key, k -> new BigDecimal("123.456"));

该方法的实现类似于上面定义的safeGet(...),但更灵活,可直接在地图实例中使用并且经过更好的测试。因此,如果可能的话,我建议改用computeIfAbsent()

【讨论】:

  • 这是一个有用的代码 sn-p。谢谢你。我不知道如何使用泛型。
【解决方案4】:

您可以使用Eclipse Collections 中的MutableMapgetIfAbsentPut(),它们返回映射到键的值或插入给定值,如果没有值映射到键,则返回给定值。

您可以使用方法引用来创建一个新的Object

MutableMap<String, Object> map = Maps.mutable.empty();
Object value = map.getIfAbsentPut("key", Object::new);

也可以直接新建一个Object

MutableMap<String, Object> map = Maps.mutable.empty();    
Object value = map.getIfAbsentPut("key", new Object());

在第一个示例中,只有在没有映射到键的值时才会创建对象。

在第二个示例中,无论如何都会创建对象。

注意:我是 Eclipse Collections 的贡献者。

【讨论】:

    【解决方案5】:

    如果在任何情况下您需要在地图中获取默认数据(如果它不存在)

    map.getOrDefault(key, defaultValue);
    

    javadocs

    【讨论】:

    • 有趣,但这并没有按照 OP 的要求将其放入地图中
    • 注意:MAP.getOrDefault(key, defaultValue) 仅在 java 8 及更高版本上受支持。
    【解决方案6】:

    编辑:请注意,下面提到的功能早已弃用,应使用CacheBuilder 代替。

    Guava 库有一个“computing map”,参见MapMaker.makeComputingMap(Function)

    Map<String, Object> map = new MapMaker().makeComputingMap(
        new Function<String, Object>() {
          public String apply(Stringt) {
            return new Object();
          }
      });
    

    如果您多次需要该函数,请将其提取到一个实用程序类中,然后像这样创建 Map(其中MyFunctions.NEW_OBJECT 是静态函数实例):

    Map<String, Object> map = new MapMaker()
        .makeComputingMap(MyFunctions.NEW_OBJECT);
    

    【讨论】:

    • MapMaker 来自 Google Guava 已死。参考:code.google.com/p/guava-libraries/wiki/MapMakerMigration
    • 我理解 Guava 人认为自动计算 Map.get() 不好,因为它违反了 Map 合同。如果您仅在内部使用计算图,那么使用LoadingCache 应该很容易。当与毫无戒心的代码共享计算地图时,我可以看到他们等待发生灾难的观点。对于该用例,应该使用 safeGet() 实用函数,就像 Thomas 在他的回答中给出的那样。
    【解决方案7】:

    也许我没有看到整个问题,但是如何使用继承或组合将此行为添加到 Map 对象?

    【讨论】:

    • 这是我发现的下一个最好的东西,是的。感谢您的回复。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-07-26
    • 2018-10-10
    • 1970-01-01
    相关资源
    最近更新 更多