如何在无 null 设计中实现 List、Set 和 Map?
当您在大多数情况下可以返回 null/空对象以避免 null 时,这很好,但是像 Collection 这样的对象呢?
在 Java 中,如果在地图中找不到 get(key)
中的 key
,Map
将返回 null
。
在这种情况下,我能想到的避免 null 的最佳方法是返回一个 Entry
对象,该对象可以是 EmptyEntry
T
。
当然,我们避免了 null
,但现在如果您不检查它是否为 EmptyEntry
,则可能会出现类转换异常。
有没有更好的方法来避免 Map
的 get(K)
中出现 null
?
为了便于论证,我们假设该语言甚至没有 null
,所以不要说只使用 nulls
。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(11)
两种可能的解决方案:
提供 contains(key) 函数。 如果为不存在的键调用 get(key) 则抛出异常。 缺点是:在 contains() 之后调用 get() 会重复操作; 效率不高。
函数式语言在类似情况下使用 Maybe。 本文介绍了如何在 Java 中实现 Maybe。< /p>
Two possible solutions:
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.
Functional languages use Maybe in similar situations. This article explains how to implement Maybe in Java.
您可以抛出“元素不存在异常”,但异常的成本很高,应该为“异常情况”保留。 地图中不存在值的情况很少见,因此它可能是一个减速带,但像往常一样,这取决于您所处的上下文。
无论哪种方式,作为建议,您应该考虑使用 contains(key)方法。 key 总是有可能被映射到 null 值,因此 get(key) 将返回 null,即使存在于您的映射中!
编辑:
看完 get() 的源代码后,我想出了一些东西(郑重声明:完全未经测试,当前时间是凌晨 01:08,我感冒了!!)
我可以强迫 V 实现某些接口(例如 MappeableElement 或类似的接口)具有 boolean isUnmappedValue() 方法,那么 get() 方法可以返回该接口的实例。
所以最终会是这样的:
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 !!)
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:
您的问题是由您正在设计的数据结构的某些不寻常的要求或场景引起的,还是只是理论上的? 如果是后者,您可能需要考虑这样一个事实: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!
返回通用
Optional
类型的实例。Return an instance of a generic
Optional<T>
type.我认为有三种可能性。
null
或 Null 对象。get
之前调用containsKey
来避免此问题。如果您担心 Null 对象是错误的类型,那么您可以将映射设计为接受 NullObjectFactory,它为您正在处理的内容创建正确类型的特殊 Null 对象(如 空对象模式)。 这样,您就可以从 Map 中
get
,而无需检查它是否包含该键,无需检查它是否返回null
,也无需捕获任何异常。There are three possibilities as I see it.
null
or a Null Object.containsKey
before you callget
.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 anull
, and without having to catch any exceptions.抛出异常。 .NET 的 KeyNotFoundException 就是这样的示例, Java 的
ArrayIndexOutOfBoundsException
< /a> 和 Python 的KeyError
。众所周知,例外是针对特殊情况的,因此用户应该在查找密钥之前检查它是否存在。
好
坏
Throw an exception. Examples of this are .NET's KeyNotFoundException, Java's
ArrayIndexOutOfBoundsException
, and Python'sKeyError
.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
Bad
比照。 上面的@Doug McClean——这听起来像Scala所说的
Option
和 Haskell 调用也许
。 这很像您所描述的,带有Entry
和EmptyEntry
——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 callsMaybe
. It's a lot like what you described, with theEntry
andEmptyEntry
-- Scala usesSome
for the validEntry
s andNone
for theEmptyEntry
.Daniel Spiewak has a good intro to
Option
, including a basic Java implementation. (Instead of hisinstanceof None
check, though, I would probably have anisNone()
method, and maybe just have oneNone
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 whateverNone<T>
you need.)我想您可以返回一个具有“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.
我知道您正在寻找 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)
从概念上讲这是一个大问题。 有用的场景之一是创建适配器,将所有调用委托给底层映射对象。 对于此适配器,将需要指定
null
对象的参数。 例如:很多工作,但它允许通用 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:A lot of work, but it allows generic NullSafe implementation.
看起来 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.