【问题标题】:Java Map implementation that returns a default value instead of null返回默认值而不是 null 的 Java Map 实现
【发布时间】:2011-06-17 12:40:21
【问题描述】:

我的代码中有一个Map<String, List<String>>,如果映射的#get() 方法返回一个空列表而不是null,我会避免潜在的空指针。 java API中有这样的东西吗?我应该扩展HashMap吗?

【问题讨论】:

  • 使用装饰器模式可能比扩展 HashMap...
  • 我不怀疑你,但为什么会更好?
  • @jk:你为什么要限制自己总是是一个HashMap?如果有时你想要一个 LinkedHashMap 怎么办?还是 ConcurrentHashMap?还是树状图?基本上,更喜欢组合而不是继承:)
  • @jk:我强烈建议使用 @Stephen C 建议的 Guava Multimap。我认为这是您真正想要的,而不是返回非空值的通用 Map不包含键时的值。

标签: java default


【解决方案1】:

如 cmets 中所述:

Guava 的计算地图概念已被 LoadingCache 取代。此外,java 8 还为 Map 接口引入了不错的 computeIfAbsent 默认方法,该方法不会破坏地图合同并具有延迟评估功能。


Guava 有一个“计算图”的想法,如果它不存在,它将执行一个函数来提供一个值。它在MapMaker.makeComputingMap 中实现;您现在可以使用 CacheBuilder - 更多详情请参阅 CacheBuilder.build

这对于你所追求的可能是矫枉过正——你最好只写一个Map 实现,一个Map(使用组合而不是扩展任何特定的实现)和然后如果它不存在就返回默认值。 get 以外的所有方法都可能只是委托给另一个地图:

public class DefaultingMap<K, V> implements Map<K, V>
{
    private final Map<K, V> map;
    private final V defaultValue;

    public DefaultingMap(Map<K, V> map, V defaultValue)
    {
        this.map = map;
        this.defaultValue = defaultValue;
    }

    @Override public V get(Object key)
    {
        V ret = map.get(key);
        if (ret == null)
        {
            ret = defaultValue;
        }
        return ret;
    }

    @Override public int size()
    {
        return map.size();
    }

    // etc
}

【讨论】:

  • 当心:这样做会破坏get() 的合同:如果地图不包含密钥,则返回null。为了保持一致,您应该覆盖containsKey()总是 返回true。我会假设 makeComputingMap() 也会这样做,但没有发现它在文档中明确说明。
  • @Mark:可能。尽管这样您在遍历键时也会遇到棘手的问题……基本上,抽象迟早会中断。 在哪里你打破它肯定是一个需要深入考虑的问题:)
  • // etc 不小,使得这个解决方案在 IMO 中不切实际。
  • 或者,您可以扩展 com.google.common.collect.ForwardingMap(来自 Google Guava),这使得 @JonSkeet 建议的实现不那么冗长。
  • Guava 的计算地图概念被 LoadingCache 取代。 Java 8 还引入了 Map 接口 nice computeIfAbsent 默认方法,该方法不会破坏地图合同并具有延迟评估功能
【解决方案2】:

在我看来,您可以简单地编写一个包装函数来获取您想要的值,如果它是 null,则返回默认值。

Map<String, List<String>> myMap = new Map<String, List<String>>();

public List<String> myGet(String key) {
    List<String> ret = myMap.get(key);
    if(ret == NULL) {
        return defaultList(); // where defaultList is a function that generates your default list.
    }
    return ret;
}

这假设您的myMap 是一个全局变量(为简单起见),如果这不是您想要的,那么您也可以将MapHashMap 扩展为MyMap 之类的东西,并且只包含这个额外的函数.这样您就可以在任何实例化MyMap 对象的地方使用它。

【讨论】:

  • 如果您需要将地图传递给期望地图的东西,这并没有多大帮助。
【解决方案3】:

@Jon's answer 是直接完成您所要求的事情的好方法。

但令我震惊的是,您可能尝试实现的是“多地图”;即从键到值集合的映射。如果是这种情况,那么您还应该查看 Guava 或 Apache commons 集合中的 multimap 类。

看看:

【讨论】:

  • +1 和 get() 在不包含密钥的 Multimap 上按合同返回一个空的 Collection ......正是 OP 想要的。具有未指定行为的 Apache Commons MultiMap 并非如此。
  • @ColinD 是的。在使用apache commons的时候,我们可以使用DefaultedMap,它在map不包含指定值的时候返回一个默认值。
【解决方案4】:

与之前的帖子类似,只是如果你想改变它的行为,你可以重写 get 方法。

Map<String, List<String>> map = new LinkedHashMap<String, List<String>>() {
    public String get(Object key) {
        List<String> list = super.get(key);
        if (list == null && key instanceof String)
           super.put(key, list = new ArrayList<String>());
        return list;
    }
}; 

【讨论】:

    【解决方案5】:

    Guava 有一种方法可以完全按照您的意愿行事。类似于Argote's answer

    Map<String, List<String>> myMap = ...
    Functions.forMap(myMap, Arrays.asList("default", "value"));
    

    【讨论】:

    • 非常漂亮的单线。 :D
    • 也许我遗漏了一些东西,但这如何创建一个具有默认值的 Map 实现?
    • 它没有。它创建一个番石榴Function,类似于MapMap 接口不允许使用默认值。
    • 好的,我认为我没有得到逻辑的飞跃。 OP 专门要求实现Map,而您说这“正是您想要的”。
    • OP 想要打破 Map 的合同,所以 OP 想要一些不是 Map 的东西。 :)
    【解决方案6】:

    感谢default 方法,Java 8 现在已经内置了 Map::getOrDefault

    Map<Integer, String> map = ...
    map.put(1, "1");
    System.out.println(map.getOrDefault(1, "2")); // "1"
    System.out.println(map.getOrDefault(2, "2")); // "2"
    

    【讨论】:

    • 这是最精简的答案,因为它不需要番石榴。而且,顺便说一句,这正是我要找的。点赞。
    • 从时间上看,这是最好的解决方案,但是来晚了(感谢 java 8!)。我喜欢它的简单。 +1
    • 既然 Java 8 已经司空见惯,应该是公认的答案。
    • 这很有用,但实际上 OP 要求地图的 #get 方法返回值,而不是使用其他方法。这些东西不一样=)
    • 这没有回答原始问题 - OP 想要“避免潜在的空指针”。 getOrDefault 的实现或多或少类似于if (get(key) != null) || containsKey(key)) return get(key) else return defaultValue;。所以如果map中包含key,但是value是null,就不会避开空指针!
    猜你喜欢
    • 1970-01-01
    • 2018-04-10
    • 2019-06-22
    • 2020-06-13
    • 2023-03-26
    • 1970-01-01
    • 2016-08-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多