一、HashMap的内部存储实现
HashMap底层实现的数据结构是哈希表。哈希表的实现一般有两种,第一种是数组(闭散列),第二种是数组+链表(开散列)。而HashMap采用的是“数组+链表”的实现,
即数组存储链表的头结点。
1、HashMap存储数据时进行put( key, value )的操作,源代码如下:
- public V put(K key, V value) {
- if (key == null)
- return putForNullKey(value);
- int hash = hash(key.hashCode()); //首先进行key.hashCode()操作,获取key的哈希值
- int i = indexFor(hash, table.length); //相当于:int i = hash % Entry[].length,Entry.length是数组长度
- for (Entry<K,V> e = table[i]; e != null; e = e.next) {
- Object k;
- if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
- V oldValue = e.value;
- e.value = value;
- e.recordAccess(this);
- return oldValue;
- }
- }
- modCount++;
- addEntry(hash, key, value, i);
- return null;
- }
注意:在Entry类里面有一个next属性,指向下一个Entry。例如,第一个键值对A进来,通过计算得到其key的hash是1,则存储:Entry[1]=A。接着,第二个键值对B进来,其key的hash也等于1,那么HashMap会这样做:B.next = A, Entry[1] = B,以此类推。由此可知,数组中存储的是最后插入的元素。
2、取出数据进行get( key )操作,源代码如下:
- 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;
- }
当key不为空时,先根据hash函数得到hash值,再根据indexFor()得到数组的索引值,接着遍历链表。如果有key值等于已存在的key值,则返回其value。
二、HashMap和HashTable的比较
HashTable和HashMap采用相同的存储机制,二者的实现基本一致,不同的是:
1. HashMap是非线程安全的,HashTable是线程安全的。
2. HashMap的键和值都可以为null,但是HashTable不行。
3. 由于线程安全的关系,HashMap的效率比HashTable要高。
4. HashMap没有使用HashTable的contains()函数,取而代之的是containsKey()和containsValue()。