如何在无 null 设计中实现 List、Set 和 Map?

发布于 2024-07-26 03:27:58 字数 575 浏览 9 评论 0 原文

当您在大多数情况下可以返回 null/空对象以避免 null 时,这很好,但是像 Collection 这样的对象呢?

在 Java 中,如果在地图中找不到 get(key) 中的 keyMap 将返回 null

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

当然,我们避免了 null,但现在如果您不检查它是否为 EmptyEntry,则可能会出现类转换异常。

有没有更好的方法来避免 Mapget(K) 中出现 null

为了便于论证,我们假设该语言甚至没有 null,所以不要说只使用 nulls

Its great when you can return a null/empty object in most cases to avoid nulls, but what about Collection like objects?

In Java, Map returns null if key in get(key) is not found in the map.

The best way I can think of to avoid nulls in this situation is to return an Entry<T> object, which is either the EmptyEntry<T>, or contains the value T.

Sure we avoid the null, but now you can have a class cast exception if you don't check if its an EmptyEntry<T>.

Is there a better way to avoid nulls in Map's get(K)?

And for argument sake, let's say this language don't even have null, so don't say just use nulls.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(11

烈酒灼喉 2024-08-02 03:27:58

两种可能的解决方案:

  1. 提供 contains(key) 函数。 如果为不存在的键调用 get(key) 则抛出异常。 缺点是:在 contains() 之后调用 get() 会重复操作; 效率不高。

  2. 函数式语言在类似情况下使用 Maybe。 本文介绍了如何在 Java 中实现 Maybe。< /p>

Two possible solutions:

  1. Provide a contains(key) function. Throw an exception if get(key) is called for a non-existent key. The downside is: calling get() after contains() duplicates operations; not efficient.

  2. Functional languages use Maybe in similar situations. This article explains how to implement Maybe in Java.

不弃不离 2024-08-02 03:27:58

您可以抛出“元素不存在异常”,但异常的成本很高,应该为“异常情况”保留。 地图中不存在值的情况很少见,因此它可能是一个减速带,但像往常一样,这取决于您所处的上下文。

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

编辑:

看完 get() 的源代码后,我想出了一些东西(郑重声明:完全未经测试,当前时间是凌晨 01:08,我感冒了!!)

  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");}

You could throw an "Element not present excepton", but exceptions are expensive, and should be reserved for "Exceptional situations". A value not being present in a map is hardly the case, so it might be a speed bump, but as usual, it depends on the context you are in.

Either way, as an advice, you should consider using the contains(key) method. There's always the posiblity that key has been mapped to the null value, so get(key) would return null, even if present in your map !!!.

Edit:

After watching get()´s source code, I came up with something ( for the record: completely untested and current time here is 01:08 AM, and I have a terrible cold !!)

  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();
   }

I you could force V to implement some interface such as MappeableElement or something like that which had a method boolean isUnmappedValue(), then the get() method could return an instance of that interface.

So it would end up in something like:

Element e = map.get(notpresentkey);

if (e.isUnmappedValue()){sysout (notpresentkey  +" is not present");}
娇纵 2024-08-02 03:27:58

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

Is your question caused by some unusual requirement or scenario for the data structure you're designing, or are you just theoreticizing? If it's the latter, you might want to consider the fact that null is conceptually indistinguishable from any other reference value (e.g. ref to some final obj denoting null), and exceptions are expensive. Unless you have a specific concern or goal making you ask this question, you're really wasting (y)our time. Cheers!

甜宝宝 2024-08-02 03:27:58

返回通用 Optional 类型的实例。

Return an instance of a generic Optional<T> type.

野心澎湃 2024-08-02 03:27:58

我认为有三种可能性。

  • 返回 null 或 Null 对象。
  • 抛出并捕获异常。
  • 或者在调用 get 之前调用 containsKey 来避免此问题。

如果您担心 Null 对象是错误的类型,那么您可以将映射设计为接受 NullObjectFactory,它为您正在处理的内容创建正确类型的特殊 Null 对象(如 空对象模式)。 这样,您就可以从 Map 中get,而无需检查它是否包含该键,无需检查它是否返回 null,也无需捕获任何异常。

There are three possibilities as I see it.

  • Return a null or a Null Object.
  • Throw and catch an exception.
  • Or avoid the issue by calling containsKey before you call get.

If you're worried about the Null Object being the wrong type, then you could design your map to accept a NullObjectFactory, that creates a special Null Object of the correct type for what you're dealing with (as in the Null Object pattern). That way you could get from a Map without having to check if it contains the key, without having to check if it returns a null, and without having to catch any exceptions.

只为一人 2024-08-02 03:27:58

抛出异常。 .NET 的 KeyNotFoundException 就是这样的示例, Java 的 ArrayIndexOutOfBoundsException< /a> 和 Python 的 KeyError

众所周知,例外是针对特殊情况的,因此用户应该在查找密钥之前检查它是否存在。

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

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

Throw an exception. Examples of this are .NET's KeyNotFoundException, Java's ArrayIndexOutOfBoundsException, and Python's KeyError.

As we all know exceptions are for exceptional situations, so users should be expected to check that a key exists before looking it up.

Good

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

Bad

try:
    return collection.get(key);
catch KeyError:
    pass # Don't care.
○闲身 2024-08-02 03:27:58

比照。 上面的@Doug McClean——这听起来像Scala所说的Option 和 Haskell 调用 也许。 这很像您所描述的,带有 EntryEmptyEntry ——Scala 使用 Some 作为有效的 Entry s 和 None 表示 EmptyEntry

Daniel Spiewak Option 进行了很好的介绍,包括基本的 Java 实现。 (不过,我可能有一个 isNone() 方法,而不是他的 instanceof None 检查,并且可能只有一个 None 实例 -由于 Java 泛型在运行时被删除,并且它实际上从未包含任何内容,因此您可以将其强制转换,或者使用“工厂”方法将其强制转换为您需要的任何 None。)

Cf. @Doug McClean above -- this sounds like what Scala calls Option and Haskell calls Maybe. It's a lot like what you described, with the Entry and EmptyEntry -- Scala uses Some for the valid Entrys and None for the EmptyEntry.

Daniel Spiewak has a good intro to Option, including a basic Java implementation. (Instead of his instanceof None check, though, I would probably have an isNone() method, and maybe just have one None instance -- since Java generics are erased at run time, and it never actually contains anything, you can cast it, or have a "factory" method that casts it, to whatever None<T> you need.)

柠檬心 2024-08-02 03:27:58

我想您可以返回一个具有“Found”布尔值的对象和一个具有找到的项目或引发异常的项目。 另一种方法是使用 TryGet 技术。

I suppose you could return an object that has a boolean for Found and an Item that has either the found item or throws an exception. Another way is to use a TryGet technique.

凯凯我们等你回来 2024-08-02 03:27:58

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

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

I understand that you are looking for an alternative to null, but the alternatives all seem to result in some Exception case, which is much more expensive (generating stacktraces,etc.) than testing for null.

So to keep in the game, return an InvalidValueException when trying to insert an invalid (ie null) value or return a NoValuePresentException on a bad get(). (all the while wishing there was a simple null test that you could perform)

海风掠过北极光 2024-08-02 03:27:58

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

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 实现。

Conceptually it is a big problem. One of useful scenarios is to create adapter which delegates all calls into underlying map object. For this adapter there will be required parameter which specify null object. 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;
   }
}

A lot of work, but it allows generic NullSafe implementation.

泼猴你往哪里跑 2024-08-02 03:27:58

看起来 Maybe 和 Option 是正确的选择。

但也需要模式匹配,以使此类的用户更简单。 这样,用户就不需要使用instanceof 和cast 来承担实时类转换异常的风险。

It looks like Maybe and Option is the way to go.

But pattern matching is also required to make it simpler for the user of this class. This way the user does not need to use instanceof and cast with the risk of a real time class cast exception.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文