HashMap的基本原理:
HashMap是基于哈希算法,然后调用get()和put()方法进行数据的读取和存入。
首先看一下HashMap的存储形式:
数据的存储put()方法:
1. 当我们将键值对传递给HashMap的put()方法时,它就会调用对象的hashCode()方法计算对应的hashCode值,根据hashCode值找到对应的bucket位置来进行存储对象。
2.此时,找到bucket位置后,不会立即去存储键值对,而是通过循环变量查询对应bucket索引后面的数据,调用equals()方法查看是否有与存入的数据的key相同的数据存在(因为HashMap中没有重复的数据存在),当找到有key值相同时,就将新的value值替换旧的value值,并返回旧的value值,如果没有相同的key存在,就直接在索引(也就是链表)前面插入该key-value键值对。如下代码:
1.public V put(K key, V value) {
2. // HashMap允许存放null键和null值。
3. // 当key为null时,调用putForNullKey方法,将value放置在数组第一个位置。
4. if (key == null)
5. return putForNullKey(value);
6. // 根据key的keyCode重新计算hash值。
7. int hash = hash(key.hashCode());
8. // 搜索指定hash值在对应table中的索引。
9. int i = indexFor(hash, table.length);
10. // 如果 i 索引处的 Entry 不为 null,通过循环不断遍历 e 元素的下一个元素。
11. for (Entry<K,V> e = table[i]; e != null; e = e.next) {
12. Object k;
13. if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
14. V oldValue = e.value;
15. e.value = value;
16. e.recordAccess(this);
17. return oldValue;
18. }
19. }
20. // 如果i索引处的Entry为null,表明此处还没有Entry。
21. modCount++;
22. // 将key、value添加到i索引处。
23. addEntry(hash, key, value, i);
24. return null;
25.}
数据读取get()方法:
我们都知道HashMap存储的值key-value键值对,所以,当对数据进行查询读取时,是根据key值进行查询。
1. 将key值给get(key)方法后,就会首先调用对象的hashCode()方法,获得此key值对应的hashCode值,根据hashCode值找
到对应的bucket位置。
2. 找到bucket位置后,就是用equals()方法开始遍历后面的一组数据。如果找到key值相同的数据,就返回,如果没有找到就
返回null.
源代码如下:
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;
}
有了上面存储时的hash算法作为基础,理解起来这段代码就很容易了。从上面的源代码中可以看出:从HashMap中get元素时,
首先计算key的hashCode,找到数组中对应位置的某一元素,然后通过key的equals方法在对应位置的链表中找到需要的元素
归纳起来简单地说,HashMap 在底层将 key-value 当成一个整体进行处理,这个整体就是一个 Entry 对象。HashMap 底层采用一个 Entry[] 数组来保存所有的 key-value 对,当需要存储一个 Entry 对象时,会根据hash算法来决定其在数组中的存储位置,在根据equals方法决定其在该数组位置上的链表中的存储位置;当需要取出一个Entry时,也会根据hash算法找到其在数组中的存储位置,再根据equals方法从该位置上的链表中取出该Entry。