为啥HashMap的运算复杂度没有考虑hash函数的复杂度? [复制]

Posted

技术标签:

【中文标题】为啥HashMap的运算复杂度没有考虑hash函数的复杂度? [复制]【英文标题】:Why is the complexity of the hash function not considered in the complexity of HashMap's operations? [duplicate]为什么HashMap的运算复杂度没有考虑hash函数的复杂度? [复制] 【发布时间】:2018-06-21 03:58:33 【问题描述】:

对于HashMap<String, String> map,每次将键值对插入映射时都会计算哈希值 -

java.lang.String#hashCode

public int hashCode() 
    int h = hash;
    if (h == 0 && value.length > 0) 
        char val[] = value;

        for (int i = 0; i < value.length; i++) 
            h = 31 * h + val[i];
        
        hash = h;
    
    return h;

因为不言自明,put操作的复杂度基本上就是hash计算的复杂度。

那么,为 put/get 操作定义 hashmap 最坏情况时间复杂度的适当方法应该是什么?

如果您从哈希冲突的角度有同样的问题,您可以在这里找到答案: Is a Java hashmap really O(1)?

【问题讨论】:

该代码无法编译(hash 来自哪里?)。另外,究竟您的编程问题/您遇到的问题是什么? @Todd 这不是重复的 - 这个问题指的是不同的东西。 @FrankSchmitt 该代码是从字符串源中提取的 - hash 是字符串的缓存哈希值。真正的存储,一些字符串(如“polygenelubricants”,将哈希为 0 导致每次调用 hashCode() 时都会计算它们!! @ravi 这不是同一个问题。请再看一遍 但我认为这里其实有这个问题的答案:***.com/questions/2771368/… 【参考方案1】:

当您计算时间复杂度作为N 的函数时,您必须首先确定N 代表什么。当我们谈论HashMap操作的复杂性时,N代表HashMap的大小(即HashMap中存储的键值对的数量)。

给定键的hashCode() 的时间复杂度不取决于HashMap 中的条目数。因此,计算hashCode() 需要O(1) 时间(假设您的示例中String 键的长度不是Map 大小的函数-我们可以构造一个奇怪的HashMap&lt;String,String&gt;,其中@放在Map 中的 987654334@th 键具有 i 字符 - 在这种边缘情况下,hashCode() 计算将花费 O(N) 时间,因此,所有 HashMap 操作将需要 O(N) 时间O(1))。

计算hashCode() 后,需要O(1) 时间来确定该键是否已存在于Map 中(因为HashMap 的每个桶中的平均条目数受常数)。

【讨论】:

你能澄清一下“因为HashMap的每个bucket中的平均条目数是由一个常量绑定的”吗? @bonnal-enzo HashMap 有一个负载因子,通常HashMap 有 1000 个桶,它最多有 750 个条目(当您尝试添加第 751 个条目时,桶的数量会增加)。因此,每个桶中的平均条目数 非常清楚,谢谢,您知道达到阈值时桶的数量通常乘以哪个因子? 2? @bonnal-enzo 是的。桶数是 2 的幂,在 HashMap 调整大小时乘以 2。【参考方案2】:

大 O 表示法是在谈论操作的复杂性。当涉及更多元素时,大多数操作会变得更复杂(即需要更多时间),并且符号描述了复杂性如何相对于元素数量增长。

使用 O(1),您是说操作与所涉及的元素数量无关。由于其自身原因,散列操作可能快或慢,但无论您有 1 个元素的 HashMap 还是它们的 googolplex,该速度都不会改变。

需要注意的是,O(1) 是摊销平均值,不能保证。最坏的情况被认为是 O(n),假设是一个每次返回相同哈希的哈希函数,但可以想象(如 cmets 中的 user889742 所建议的那样)有一个故意坏的哈希码函数,其性能甚至比那个。

【讨论】:

完全有可能每个元素的大小与元素的总数有关。 (例如,序列号,随着元素数量的增长需要更多的数字。)在这种情况下,这个哈希函数会随着元素数量的增长而变慢。 您是绝对正确的,某些哈希码实现可能会导致 HashMap 在插入时不具有 O(1) 复杂度,O(1) 是假定良好哈希码实现的平均值。最坏情况通常被认为是 O(n),其中所有哈希值都相同。我不确定是否考虑过故意使用错误的哈希码;执行无限循环的性能很差。【参考方案3】:

您必须知道(1) 的用途。这是数组中元素的数量。插入 HashMap 的成本不会根据映射中的元素数量而改变(假设您已在结构的整个生命周期内分摊了插入成本。)

您正确计算 String 的 hashCode 是 O(n) 其中 n 是字符串的长度。但是,一旦拥有它,无论您使用多少次,它都将永远拥有。所以它的成本被认为是恒定的。

【讨论】:

以上是关于为啥HashMap的运算复杂度没有考虑hash函数的复杂度? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

HashMap下标计算详解

HashMap之key的hash函数

为啥HashMap的负载因子是0.75

HashMap源码分析

Hashmap jdk7 死循环

HashMap的默认长度为什么是16?