Java如何实现哈希表?

有谁知道Java如何实现其哈希表(HashSet或HashMap)? 鉴于人们可能希望将各种类型的对象放入哈希表中,似乎很难提出一种适用于所有情况的哈希函数。

HashMap和HashSet非常相似。 实际上,第二个包含第一个实例。

HashMap包含一个桶数组以包含其条目。 数组大小始终为2的幂。如果未指定其他值,则最初有16个桶。

当你在其中放入一个条目(键和值)时,它决定将插入条目的桶从其键的哈希码计算它( 哈希码不是它的内存地址,哈希不是模数 )。 不同的条目可能会在同一个存储桶中发生冲突,因此它们将被放入列表中。

将插入条目,直到达到加载因子。 默认情况下,此因子为0.75 ,如果您不确定自己在做什么,建议不要更改它。 0.75作为加载因子意味着16个桶的HashMap只能包含12个条目( 16 * 0.75 )。 然后,将创建一个桶arrays,使之前的大小加倍。 所有条目将再次放入新数组中。 这个过程被称为rehashing ,并且可能很昂贵。

因此,如果您知道将插入多少条目,最佳实践是构造一个指定其最终大小的HashMap:

new HashMap(finalSize); 

例如,您可以检查HashMap的来源。

Java依赖于每个类的hashCode()方法的实现来均匀地分布对象。 显然,糟糕的hashCode()方法会导致大型哈希表的性能问题。 如果类没有提供hashCode()方法,则当前实现中的默认值是返回内存中对象地址的某些函数(即散列)。 引用API文档:

尽可能合理,Object类定义的hashCode方法确实为不同的对象返回不同的整数。 (这通常通过将对象的内部地址转换为整数来实现,但JavaTM编程语言不需要此实现技术。)

实现HashMap有两种常规方法。 不同之处在于如何处理碰撞。

第一种方法是一个Java用户,它使HashMap中的每个存储桶都包含一个单独链接的列表。 为此,每个桶都包含一个Entry类型,它缓存hashCode,具有指向键的指针,指向值的指针以及指向下一个条目的指针。 在Java中发生冲突时,会向列表中添加另一个条目。

处理碰撞的另一种方法是简单地将物品放入下一个空桶中。 这种方法的优点是它需要更少的空间,但是,它使删除变得复杂,就好像被移除的项目之后的桶不是空的,必须检查该项目是在正确还是错误的桶中,并且移动项目如果它最初与被移除的物品相撞。

用我自己的话:

创建Entry对象以保存Key和Value的引用。

HashMap有一个Entry数组。

给定条目的索引是key.hashCode()返回的哈希值

如果存在冲突(两个键给出相同的索引),则该条目存储在现有条目的.next属性中。

这就是具有相同散列的两个对象如何存储到集合中的方式。

从这个答案我们得到:

  public V get(Object key) { if (key == null) return getForNullKey(); int hash = hash(key.hashCode()); for (Entry e = table[indexFor(hash, table.length)]; e != null; e = e.next) { Object k; if (e.hash == hash && ((k = e.key) == key || key.equals(k))) return e.value; } return null; } 

如果我出错了,请告诉我。