我无法理解HashMap的工作模式。请帮助理解它。
假设我们有两个对象 Obj1 和 Obj2 具有与 1212 相同的Hashcode。现在当我们运行" =="并且等于它返回false。
现在我使用 ValueObj1 和 Valueobj2 作为HashMap中的值,分别为 Obj1 和 Obj2 。 我相信这两个值都将保存在与List 相同的存储桶中。
我的问题是HashMap如何为Obj2选择 Valueobj2,为Obj1选择ValueObj1 。假设有n ..这样的对象和值。 这个键怎么样 - >值关联在内部工作,即使哈希码相同但值不同。
假设两个条件等于未被覆盖和覆盖 。
答案 0 :(得分:12)
HashMap
/ HashSet
每个桶实现一个键列表(在Map
的情况下与值一起)。如果多个密钥共享相同的hashCode
值,则会将它们放在此列表中。
因此,搜索方法首先提取查询键的hashCode
,然后迭代相应的列表,直到equals
方法成功。如果是HashSet
,则表示找到key
,如果是HashMap
,则返回元组的另一侧:值。
HashMap
的记忆就像:
+--------+--------+--------+--------+
| 00 | 01 | 10 | 11 |
+--------+--------+--------+--------+
| | | |
k00/v00 _ k06/v06 _
| |
k08/v08 k14/v14
| |
k04/v04 _
|
_
你看到的是四个水桶的顶部。每个存储桶都有一个列表(下面的项目),用于存储密钥元组(k
)和值(v
)。由于此处只有四个存储桶,因此哈希算法使用模4运算,因此值k06
的密钥v06
将放置在存储区06 mod 4 = 02
中,从而10
。如果第二个密钥k14
与14 mod 4 = 02
10
一起添加,则会将其添加到列表中。
由于值也与它一起存储,因此可以执行快速查找操作。因此,键与值一起存储。
你注意到,迭代(链接)列表是一项昂贵的操作。但是HashMap
的意思是,人们希望使用正确术语(共享同一个桶的密钥数)的哈希冲突的数量非常少。通常,人们可能期望每个桶有两个或三个元素。因此,通过在恒定时间内选择正确的桶来实现性能提升,搜索存储桶需要线性时间(或者如果密钥有完整的排序,可以实现(平衡)二进制)树以对数时间搜索)。但最糟糕的是,HashMap
可以实现与ArrayList
/ LinkedList
条目相同的性能,但鉴于哈希 -function已经设计得很好,赔率非常低。
答案 1 :(得分:11)
You can always look at the code.
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;
}
通过哈希对存储桶进行分区会减少使用equals
比较进行线性搜索的大小。因此,您可以看到为哈希码返回固定值有多么有害。有关良好哈希码计算的提示,请参阅this。
答案 2 :(得分:3)
HashMap
的工作原理是根据密钥的哈希值将其内容划分为桶。每个存储桶依次包含一个条目列表,一个由密钥和值组成的条目。
我们想说我们想在地图中查找x
。我们计算x.hashCode()
并选择适当的桶。然后,我们遍历存储桶列表并选择e
等于e.key
的条目x
。然后我们返回e.value
。
伪代码:
class Map {
class Entry {
Object key, value;
}
List<List<Entry>> buckets;
Object get(Object key) {
List<Entry> bucket = buckets.get(key.hashCode() % buckets.size());
for (Entry entry : bucket) {
if (Object.equals(key, entry.key) return entry.value;
}
return null;
}
}
(免责声明:使用%来计算一个桶指数过于简单化,并不会按原样运作;它只是传达了一般的想法)
答案 3 :(得分:0)
hashcode()
方法并计算哈希码。此hashcode
用于查找用于存储Entry对象的数组索引。
indexFor(hash,table.length)
用于计算表数组中用于存储Entry对象的精确索引。
具有相同hashcode
的两个关键对象(称为碰撞)
在哈希映射中,bucket使用简单的链表来存储对象。
如果两个密钥具有相同的哈希码,则将键值对存储在与现有密钥相同的桶中。
当具有相同哈希码的两个键存储在hashmap中时,如何检索值对象? 使用hashcode wo转到右侧桶并使用equals我们在桶中找到正确的元素然后返回它。
HashMap get()函数
如果key不为null,则它将在key对象上调用hashfunction。
int hash = hash(hashValue)
hashvalue
用于查找存储Entry对象的存储区位置。条目对象存储在存储桶中,如(hash,key,value,bucketindex)
。
答案 4 :(得分:0)
将两个对象与==
进行比较并不是一个好主意,因为它会检查两个对象是否实际上是指向内存中相同对象的链接。
Wikipedia上有关于哈希表的好article。 java中的Hashmap
内部有一个“桶”数组。
当您添加新对<key, value>
(或在您的情况下为<obj1, valueObj1>
)时,根据obj1.hashcode()
计算存储桶编号。此对添加到选定的存储桶中,该存储桶内部为LinkedList
,用于存储实际的对<key, value>
。
当您尝试使用密钥对象valueObj1
搜索obj1
时,哈希映射计算该对所在的桶号,并迭代所有LinkedList
个元素比较密钥equals()
。如果equals()
立即返回true
,则表示找到了我们要查找的元素。