Java如何实现哈希表?

时间:2009-10-29 23:44:56

标签: java algorithm

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

5 个答案:

答案 0 :(得分:21)

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

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

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

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

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

new HashMap(finalSize);

答案 1 :(得分:8)

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

答案 2 :(得分:7)

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

  

尽可能合理,   class定义的hashCode方法   对象确实返回不同的整数   对于不同的对象。 (这是   通常通过转换实现   对象的内部地址   变成一个整数,但是这个   实施技术不是   JavaTM编程所要求的   语言。)

答案 3 :(得分:2)

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

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

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

答案 4 :(得分:1)

用我自己的话说:

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

HashMap有一个Entry的数组。

给定条目的索引是key.hashCode()

返回的哈希值

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

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

this回答我们得到:

   public V get(Object key) {
       if (key == null)
           return getForNullKey();
       int hash = hash(key.hashCode());
       for (Entry<K,V> 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;
   }

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