【问题标题】:Return "null" on primitive return type function?在原始返回类型函数上返回“null”?
【发布时间】:2010-07-22 19:44:15
【问题描述】:

我有一个函数,它返回给定键的int 值(来自HashMap<String, Integer>)。如果密钥不存在,我想返回调用者可以检查的内容。似乎,最常见的是,这将是“如果键不存在则返回 -1”之类的东西。但是,在我的情况下,我不能为此目的保留 -1,因为负数是 确实 存在的键的可行值。

我能想到的唯一其他选择如下:

  1. 将返回类型更改为Integer 包装类并检查null
  2. 返回一些不太可能的东西,例如Integer.MIN_VALUE
  3. 创建一个额外的 boolean keyExists(String key) 函数,该函数应始终首先调用
  4. 切换到float 并改用NaN

我是用 Java 写的,但欢迎来自相似语言背景的人发帖。谢谢!

【问题讨论】:

  • #4 很有趣。你如何检查NaN? NaN != NaN 为真。
  • Float.isNaN(float) ;) 恕我直言,浮动几乎从来都不是一个好主意。尝试 long 或 double。

标签: java


【解决方案1】:

在我看来,第一个选项是最干净的。像 -1 和 Integer.MIN_VALUE 这样的幻数有点混乱,尤其是当值不直观时。这是一个惯用的 C 解决方案,Java 开发人员会因此而讨厌你。如果这只发生在“异常”情况下,根据您实际使用的情况,您还可以抛出“KeyNotFoundException”。

【讨论】:

  • 我返回了一个 int,但如果找不到键(然后有一个 keyExists(key) 函数)则抛出运行时异常。您认为使用 Integer 会是更好的解决方案吗?它可能会更有效,因为我的 HashMap 已经将值存储为整数......
  • 不要抛出运行时异常(从 RuntimeException 派生的东西)。使用常规的检查异常。如果你想让你的程序多线程,请记住,在使用两个单独的函数调用查找值之前必须调用 keyExists 函数会产生竞争条件,除非你在每次调用它之前锁定某个对象,或者只是简单地调用它省略 keyExists 部分并将调用包装在 KeyNotFoundException 的 try/catch 块中。
【解决方案2】:

我投票赞成在这里抛出一个检查异常。一般来说,我不喜欢检查异常,但在这种情况下,这似乎是一件合理的事情。值不存在的情况需要与值存在的情况分开处理,使用异常可以明确这一点。

实际上,您添加 keyExists 方法的想法是您列出的最佳想法,因为它不依赖于用户知道寻找特殊值。如果你这样做,那么你可以抛出一个异常作为备份计划,以防映射内容在调用 keyExists 和检索之间发生变化。

【讨论】:

  • 我认为这将是一个非常好的解决方案,不知何故,我从未想过。但是,在我的特殊情况下,性能会有点问题,所以我想我会避免异常。
  • @mxrider:性能是一个问题,所以您想避免异常?嗯,什么?对我来说,这听起来像是过早/被误导的“优化”。我会说尽可能编写最干净的代码,然后使用分析器检查它是否存在实际的性能问题。
  • 我想我应该改一下。在性能很重要的情况下(例如在循环或计时器中)依赖 try/catch 可能会导致瓶颈。实际上,我最终需要使用 keyExists() 函数,如果找不到密钥,我的 getValue() 函数会抛出一个特殊的 RuntimeException——这样我就不需要 try/catch,但我仍然遵循最好的实践。
  • 嗯,我仍然不相信循环内的 try-catch 会自动产生瓶颈;我认为在假设它是这样之前应该测量它!但无论如何,使用单独的 keyExists() 方法听起来很干净。
【解决方案3】:

虽然它在很大程度上取决于应用程序和已知限制,但我更喜欢上面的#1。原因是因为它是明确的 - 根据我的经验,明确的情况总是比隐含的情况更好 - 例如 MIN_VALUE。

另一种选择是有一个包装器对象,它包含作为原始 int 的结果值,但也有一个状态值或类似的东西。所以它可能是这样的:

public class Wrapper {
    private int value = Integer.MIN_VALUE;
    private boolean isValid;
    // appropriate getters and setters etc.
}

您还可以包含一个枚举而不是布尔值来代替一个状态,具体取决于问题的复杂性。如果您看到此元素包含复合信息(由单个键键入的信息集或类似内容),则容器对象或状态对象在这些情况下可能非常有价值。

【讨论】:

    【解决方案4】:

    float 是不安全的,因为并非所有 int 值都可以准确地表示为 int 值。 long 是一个更好的选择,因为所有 int 值都可以安全地存储,并且您将有更多未找到的值。

    如果您使用的是 Map,那么无论如何您都有一个 Integer 对象,那么为什么不返回它(或 null)

    如果您真的想使用原语(为了提高效率??)我建议您改用TObjectIntHashMap。这使用原始 int 值。要检查是否有缺失,您必须单独检查 containsKey()。

    【讨论】:

      【解决方案5】:

      典型的有两种情况:

      1. 您对 找到一个特定的值 钥匙。
      2. 您不确定某个键是否 存储了一个值。

      如果假设 1 是错误的,那么它很可能表明存在错误,因此请在访问方法中使用断言保护您的假设。

      如果 2 适用,您如何确定返回的内容?所以提供“keyExists”之类的测试方法,并在访问地图之前绝对知道。

      第一种情况不需要检查,这样更干净,但是如果你错了,访问会失败。 对于 -1 或 Integer.MIN_VALUE 等特殊情况,很难确定它们不会被使用。

      Haskell、Scala 和 C# 等语言具有特殊类型来传达值的存在/不存在,这比使用 null 好得多(如果您忘记测试,您可能会得到 NullPointerException)

      【讨论】:

        【解决方案6】:

        选项 5:抛出异常

        虽然在这种情况下,我会投票支持使用 Integer 类。

        【讨论】:

          【解决方案7】:

          我认为你应该结合选项#1 和#3,因为这与底层 HashMap 的行为是一致的。来自HashMap的get method documentation

          返回此标识哈希映射中指定键映射到的值,如果映射不包含此键的映射,则返回 null。返回值为 null 并不一定表示该映射不包含该键的映射;映射也有可能将键显式映射为空。 containsKey 方法可用于区分这两种情况。

          【讨论】:

            【解决方案8】:
            1. 将返回类型更改为整数包装类并检查是否为空。

            这是更常见的选项。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2015-01-21
              • 2020-01-20
              • 2019-02-27
              • 2019-05-08
              • 1970-01-01
              • 2016-05-11
              相关资源
              最近更新 更多