有谁知道Java如何实现其哈希表(HashSet或HashMap)?鉴于人们可能希望将各种类型的对象放入哈希表中,似乎很难提出一种适用于所有情况的哈希函数。
答案 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;
}
如果我出错了,请告诉我。