如何在null自由设计中实现List,Set和Map?

时间:2009-07-02 03:51:13

标签: java architecture collections null

在大多数情况下可以返回null / empty对象以避免空值,这很棒,但是像对象这样的集合呢?

在Java中,如果在地图中找不到Map中的null,则key会返回get(key)

在这种情况下,我能想到避免null的最佳方法是返回Entry<T>对象,该对象可以是EmptyEntry<T>,也可以包含值T }。

当然我们避开了null,但是如果你不检查它是EmptyEntry<T>,你现在可以有一个类强制转换异常。

是否有更好的方法可以避免null Map get(K)中的null

为了论证,让我们说这种语言甚至没有nulls,所以不要只说使用{{1}}。

11 个答案:

答案 0 :(得分:5)

两种可能的解决方案:

  1. 提供包含(键)功能。如果为不存在的密钥调用get(key),则抛出异常。缺点是:在contains()重复操作之后调用get();效率不高。

  2. 功能语言在类似情况下使用Maybe。本文解释了how to implement Maybe in Java

答案 1 :(得分:2)

你可以抛出一个“元素不存在除外”,但异常是昂贵的,应该保留为“异常情况”。地图中不存在的值几乎不是这样,因此它可能是一个减速带,但通常情况下,它取决于您所处的背景。

无论哪种方式,作为建议,您应该考虑使用contains(key)方法。键总是被映射到空值的可能性,因此get(key)将返回null,即使存在于地图中!!!。

编辑:

在看了get()的源代码之后,我想出了一些东西(记录:完全未经测试,当前时间是01:08 AM,我感冒了!)

  314       public V get(Object key) {
  315           if (key == null)
  316               return getForNullKey();
  317           int hash = hash(key.hashCode());
  318           for (Entry<K,V> e = table[indexFor(hash, table.length)];
  319                e != null;
  320                e = e.next) {
  321               Object k;
  322               if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
  323                   return e.value;
  324           }
                //This could be instanced by reflection (bad idea?)
  325           return new MappeableElementImpl();
   }

我可以强制V实现某些接口,例如MappeableElement或类似具有方法boolean isUnmappedValue()的接口,然后get()方法可以返回该接口的实例。

所以最终会出现类似的结果:

Element e = map.get(notpresentkey);

if (e.isUnmappedValue()){sysout (notpresentkey  +" is not present");}

答案 2 :(得分:2)

您的问题是由您正在设计的数据结构的一些不寻常的要求或场景引起的,还是您只是在理论化?如果是后者,您可能需要考虑以下事实:null在概念上与任何其他参考值无法区分(例如,引用表示null的某些最终obj),并且异常是昂贵的。除非你有特定的关注或目标让你问这个问题,否则你真的在浪费我们的时间。干杯!

答案 3 :(得分:1)

抛出异常。例如.NET的KeyNotFoundException,Java的ArrayIndexOutOfBoundsException和Python的KeyError

众所周知,例外情况适用于特殊情况,因此在查找密钥之前,应该要求用户检查密钥是否存在。

不可

if collection.contains(key):
    return collection.get(key);

<强>为

try:
    return collection.get(key);
catch KeyError:
    pass # Don't care.

答案 4 :(得分:1)

返回通用Optional<T>类型的实例。

答案 5 :(得分:1)

我认为有三种可能性。

  • 返回null或空对象。
  • 投掷并捕获异常。
  • 或者在致电containsKey之前致电get来避免此问题。

如果您担心Null对象是错误的类型,那么您可以设计您的地图以接受NullObjectFactory,它为您正在处理的内容创建一个正确类型的特殊Null对象(如{ {3}})。这样你就可以从Map中get而不必检查它是否包含密钥,而不必检查它是否返回null,而不必捕获任何异常。

答案 6 :(得分:1)

比照。 @Doug McClean上面 - 这听起来像Scala称之为Option而Haskell称之为Maybe。它与您描述的内容非常相似,EntryEmptyEntry - Scala使用Some表示有效的EntryNone表示EmptyEntry }}。

Daniel Spiewak有a good intro to Option,包括基本的Java实现。 (但不是他的instanceof None检查,我可能会有一个isNone()方法,也许只有一个None实例 - 因为Java泛型在运行时被删除,而且它永远不会实际上包含任何东西,你可以将它强制转换,或者使用“工厂”方法将其强制转换为你需要的任何None<T>。)

答案 7 :(得分:0)

我想你可以返回一个对象,该对象具有Found的布尔值和一个具有找到的项目或抛出异常的Item。另一种方法是使用TryGet技术。

答案 8 :(得分:0)

我知道您正在寻找null的替代方法,但是替代方案似乎都会导致一些异常情况,这比测试null要贵得多(生成堆栈跟踪等)。

因此,为了保持游戏,在尝试插入无效(即null)值时返回InvalidValueException,或者在错误的get()上返回NoValuePresentException。 (一直希望你可以执行一个简单的空测试)

答案 9 :(得分:0)

从概念上讲,这是一个大问题。一个有用的场景是创建适配器,将所有调用委托给底层映射对象。对于此适配器,将需要指定null对象的参数。 E.g:

class MapAdapter<K,V> implements Map<K,V> {
    private Map<K,V> inner = new HashMap<K,V>();
    private final V nullObject;

    private MapAdapter(V nullObject) {
        this.nullObject = nullObject;
    }

    public static <K,V> Map<K,V> adapt(Map<K,V> mapToAdapt, V nullObject) {
        MapAdapter<K,V> adapter = new MapAdapter<K,V>(nullObject);
        adapter.inner.addAll(mapToAdapt);
        return adapter;
    }


    //Here comes implementation of methods delegating to inner.


   public V get(K key) {
       if (inner.containsKey(key)) {
           return inner.get(key);
       }
       return nullObject;
   }
}

很多工作,但它允许通用的NullSafe实现。

答案 10 :(得分:0)

看起来好像和选择是可行的方式。

但是还需要模式匹配才能使这个类的用户更简单。这样,用户不需要使用instanceof和cast,而存在实时类转换异常的风险。