【问题标题】:Map Lookup Efficiency of TestForNullTestForNull 的地图查找效率
【发布时间】:2015-04-30 23:24:53
【问题描述】:

Referencing a previous answer to a question on SO, 有一个方法叫做TestForNull。这是我在被告知可以提高效率之前的原始代码:

我的原始代码:

for (int i = 0; i < temp.length; i++) {
            if (map.containsKey(temp[i]))
                map.put(temp[i], map.get(temp[i]) + 1);
            else
                map.put(temp[i], 1);

在这个 sn-p 中,我对地图进行了三个查找。有人告诉我这可以通过一次查找来完成,所以我最终在 SO 上寻找答案并找到了链接的答案,并将我的代码修改为如下所示:

我修改后的代码:

for (int i = 0; i < temp.length; i++) {
            Integer value = map.get(temp[i]); 
            if (value != null)
                map.put(temp[i], value + 1);
            else
                map.put(temp[i], 1);
        }

尽管看起来更好,但对我来说似乎是两次查找,而不是一次。我想知道是否有一个只使用一个的实现,以及是否可以在不使用第三方库的情况下完成。如果有帮助,我将在我的程序中使用 HashMap。

【问题讨论】:

  • 嗯,也许他们的意思只是 get() 是一个查找?我看不出有什么办法可以进一步缩短它。
  • 你的两个代码没有区别,真的;或者更确切地说,你无法分辨。也许.containsKey() 对于这个Map 实现 做了一个完整的检索,也许它没有。在那之后,这只是一个选择问题。但我个人会选择第二种解决方案,即您修改后的代码。
  • @fge 即使我减少了查找次数?如果它对答案有帮助,我正在使用 HashMap。
  • 您担心什么需要很长时间?计算哈希码或计算哈希码后找到一个空白点?如果是第一个,那么您可以覆盖 hashcode 函数以缓存其结果。如果是第二个,您可以增加底层数组的大小。你也可以两者都做。
  • @BenjyKessler 调用get 然后put 执行两个单独的键查找。对于哈希映射,这是两个 O(1) 操作,但它仍然涉及两次调用 hashCode() 并可能搜索匹配的存储桶两次。对于树形图,这是两个 O(log n) 操作。 OP 认为这可以改进是正确的。在实践中,这并不重要,但对我来说,这种问题显示出良好的直觉。如果可以避免问题,为什么还要解决它?

标签: java performance dictionary lookup


【解决方案1】:

Java 8 向 Map 接口添加了许多 default 方法,这些方法可能会有所帮助,包括 merge

map.merge(temp[i], 1, v -> v + 1);

还有compute

map.compute(temp[i], (k, v) -> v == null ? 1 : v + 1);

HashMap's implementations of these methods 进行了适当优化,以有效地仅执行单个键查找。 (奇怪的是,TreeMap 不能这样说。)

【讨论】:

  • 默认实现做同样的事情,但我希望大多数Map 实现,特别是包括HashMapTreeMap,都有更智能的实现。
  • 是的,当然,你是对的。 Map 的实现无法避免两次查找,但HashMap 确实更聪明。
  • @LouisWasserman 是正确的。 HashMapTreeMap 都提供优化的计算实现。在TreeMap 的情况下,它从AbstractMap 继承了一个优化的实现,这仍然比2 次调用要好很多。 grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/…grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/…
  • 我显然对 grepcode 中的链接感到困惑。你是对的,TreeMap 没有优化的实现。这很奇怪,因为这样做非常简单。
  • 我将不得不更多地研究 lambda 表达式及其工作原理。您提供给我的答案是一个查找还是两个的改进版本?我看到了这个答案的先前版本,表明它与我之前所做的并没有太大不同。鉴于我需要做的事情,我认为使用 Java 8 是可以的(我很感激!)。
【解决方案2】:

@John Kugelman 的回答是最好的(只要你会使用 java 8)。

第一个示例有 3 次 map 调用的最坏情况(在值已经存在的情况下):

  1. 包含密钥
  2. 得到

第二个例子总是有 2 个调用(和一个空检查):

  1. 得到

所以你基本上是在用containsKey 换取空支票。

HashMap 中,这些操作大致是恒定的时间,假设散列码分布良好(并且分布与HashMap 的大小配合得很好)。其他Map 实现(例如TreeMap)具有log(n) 执行时间。即使在HashMap 的情况下,空检查也会比containsKey 更快,从而使第二个选项成为赢家。但是,除非您的散列码分布不佳(或者这是您的应用程序正在做的唯一事情)或执行不佳的相等检查,否则您不太可能有可测量的差异。

【讨论】:

    猜你喜欢
    • 2014-01-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-10-14
    • 2021-12-11
    • 2016-12-25
    相关资源
    最近更新 更多