HashMap源码分析

HashMap的数据结构是数组and链表

一. HashMap继承自AbstractMap类,实现了Map接口。

public class HashMap<K, V> extends AbstractMap<K, V> implements Map<K, V>, Cloneable, Serializable

 

二. HashMap中定义的属性

 1 // 默认初始容量必须是2的幂
 2 static final int DEFAULT_INITIAL_CAPACITY = 16;
 3 
 4 // 最大容量(2的30次方,传入容量过大将被这个值代替)
 5 static final int MAXIMUM_CAPACITY = 1<<30;
 6 
 7 // 默认装载因子
 8 static final float DEFAULT_LOAD_FACTOR = 0.75f;
 9 
10 // 存储数据的Entry数组,长度为2的幂
11 transient Entry[] table;
12 
13 // map中保存的键值对的数量
14 transient int size;
15 
16 // 需要调整大小的极限值(容量*装载因子)
17 int threshold;
18 
19 // 装载因子
20 final float loadFactor;
21 
22 // map结构被改变的次数
23 transient volatile int modCount;

 

三. HashMap的构造方法

// 使用默认的容量及装载因子构造一个空的HashMap
public HashMap(){
    this.loadFactor = DEFAULT_LOAD_FACTOR;
    threshold = (int)(DEFAULT_INITIAL_CAPACITY*DEFAULT_LOAD_FACTOR);
    table = new Entry[DEFAULT_INITIAL_CAPACITY];
    init();  
}

// 根据给定的初始容量and装载因子创建一个空的HashMap
// 初始容量小于0或装载因子小于等于0将报异常
public HashMap(int initialCapacity, float loadFactor){
    if(initialCapacity<0)
        throw new IllegalArgumentException("Illegal initial capacity:"+initialCapacity);
    if(initialCapacity>MAXIMUM_CAPACITY)
        initialCapacity = MAXIMUM_CAPACITY;
    if(loadFacor<=0 || Float.isNaN(loadFactor))
        throw new IllegalArgumentException("Illegal load factor: "+loadFactor);
    
    int capacity = 1;
    while(capacity<initialCapacity)
        capacity <<= 1;
    this.loadFactor = loadFactor;
    threshold = (int) (capacity * loadFactor);
    table = new Entry[capacity];
    init();
}


// 根据指定的容量创建一个空的HashMap
public HashMap(int initialCapacity){
    this(initialCapacity, DEFAULT_LOAD_FACOR);
}

// 通过传入的map创建一个HashMap,容器为默认容量16和(map.size()/DEFAULT_LOAD_FACTOR)+1的较大者,装载因子为默认值
public HashMap(Map<? extends K, ? extends V> m){
    this(Math.max((int)(m.size()/DEFAULT_LOAD_FACTOR)+1, DEFAULT_INITIAL_CAPACITY),DEFAULT_LOAD_FACTOR);
    putAllForCreate(m);
}

四. Map.Entry接口定义的方法

K getKey();     // 获取Key
V getValue();   // 获取Value

V setValue(); //设置Value
boolean equals(Object o); // 定义equals方法用于判断两个Entry是否相同
int hashCode();// 定义获取hashCode方法

五. HashMap.Entry类的具体实现

 1   static class Entry<K,V> implements Map.Entry<K,V> {
 2         final K key;
 3         V value;
 4         Entry<K,V> next;  //对下一个节点的引用
 5         final int hash;//哈希值
 6 
 7         Entry(int h, K k, V v, Entry<K,V> n) {
 8             value = v;
 9             next = n;
10             key = k;
11             hash = h;
12         }
13 
14         public final K getKey() {
15             return key;
16         }
17 
18         public final V getValue() {
19             return value;
20         }
21 
22         public final V setValue(V newValue) {
23         V oldValue = value;
24             value = newValue;
25             return oldValue;    //返回的是之前的Value
26         }
27 
28         public final boolean equals(Object o) {
29             if (!(o instanceof Map.Entry))  //先判断类型是否一致
30                 return false;
31             Map.Entry e = (Map.Entry)o;
32             Object k1 = getKey();
33             Object k2 = e.getKey();
34             // Key相等且Value相等则两个Entry相等
35             if (k1 == k2 || (k1 != null && k1.equals(k2))) {
36                 Object v1 = getValue();
37                 Object v2 = e.getValue();
38                 if (v1 == v2 || (v1 != null && v1.equals(v2)))
39                     return true;
40             }
41             return false;
42         }
43         // hashCode是Key的hashCode和Value的hashCode的异或的结果
44         public final int hashCode() {
45             return (key==null   ? 0 : key.hashCode()) ^
46                    (value==null ? 0 : value.hashCode());
47         }
48         // 重写toString方法,是输出更清晰
49         public final String toString() {
50             return getKey() + "=" + getValue();
51         }
52 
53         /**
54          *当调用put(k,v)方法存入键值对时,如果k已经存在,则该方法被调用(为什么没有内容?)
55          */
56         void recordAccess(HashMap<K,V> m) {
57         }
58 
59         /**
60          * 当Entry被从HashMap中移除时被调用(为什么没有内容?)
61          */
62         void recordRemoval(HashMap<K,V> m) {
63         }
64     }

 

六. put()方法

 

public V put(K key, V value){
    if(key==null)
        return putForNullKey(value);
    int hash = hash(key.hashCode());
    int i = indexFor(hash, table.length);  // 下标

// 得到索引后,探测table[i]所在的链表,若发现key与传入的key相同,则替换并返回oldValue;若找不到,则通过
addEntry(hash, key, value, i)添加新的对象。
    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;
}

 

hash()方法:该方法的主要作用是,防止质量较差的哈希函数带来过多的冲突问题。Java中int值占4个字节,即32位。根据这32位值进行移位、异或运算得到一个值。

static int hash(int h){
     h ^= (h>>>20)^(h>>>12);
    return h^(h>>>7)^(h>>>4);
}

indexFor()方法:返回hash值和table数组长度减1的与运算。为什么-1:这样可以保证结果的最大值为length-1,不会产生数组越界的问题。

 static int indexFor(int h, int length) {
     return h & (length-1);
 }

 

七. get()方法

 1 public V get(Object key){
 2     if(key==null)
 3         return getForNullKey();
 4     int hash = hash(key.hashCode());
 5     for(Entry<K, V> e = table[indexFor(hash, table.length)]; e!=null; e=e.next){
 6         Object k;
 7         if(e.hash==hash && ((k==e.key)==key || key.equals(k)))
 8             return e.value;
 9     }
10     return null;
11 }

getForNullKey()方法:这是一个私有方法,只有在get中被调用。该方法判断table[0]中的链表是否包含key为null的元素,包含则返回value,否则返回null。为什么遍历table[0]的链表?因为key为null时获得的hash值为0。

1     private V getForNullKey() {
2         for (Entry<K,V> e = table[0]; e != null; e = e.next) {
3             if (e.key == null)
4                 return e.value;
5         }
6         return null;
7     }

 

posted on 2017-04-10 17:48  一个不会coding的girl  阅读(253)  评论(0编辑  收藏  举报

导航