HashMap(1)

来源:互联网 发布:淘宝考试女士内衣 编辑:程序博客网 时间:2024/06/13 20:16

http://www.cnblogs.com/skywang12345/p/3310835.html

1.(1)HashMap实例受两个参数影响,初始容量和加载因子(桶数默认16和满的程度0.75

   (2)非线程安全的

2.哈希表是由数组+链表组成的,长度为16的数组,每个元素存链表的表头节点。每个bucket可以通过hash(key)%len

得。Hashmap其实也是一种线性的数组实现的。

HashMap有个静态内部类Entry(key,value,next) 实现了Map.Entry<K,V>接口,Entry包含一对键值和next指针,所以HashMap可以看成是Entry[]数组。(下图来自他人)



HashMap内部字段:table(Entry[]数组),size(int),threshold(int容量*加载因子),loadFactor(float加载因子),modCount(int 实现fail-fast机制)

Entry内部字段:next(Entry),hash(int),key,value

static class Entry<K,V> implements Map.Entry<K,V> {    final K key;    V value;    // 指向下一个节点    Entry<K,V> next;    final int hash;    // 构造函数。    // 输入参数包括"哈希值(h)", "键(k)", "值(v)", "下一节点(n)"    Entry(int h, K k, V v, Entry<K,V> n) {        value = v;        next = n;        key = k;        hash = h;    }    public final K getKey() {        return key;    }    public final V getValue() {        return value;    }    public final V setValue(V newValue) {        V oldValue = value;        value = newValue;        return oldValue;    }    // 判断两个Entry是否相等    // 若两个Entry的“key”和“value”都相等,则返回true。    // 否则,返回false    public final boolean equals(Object o) {        if (!(o instanceof Map.Entry))            return false;        Map.Entry e = (Map.Entry)o;        Object k1 = getKey();        Object k2 = e.getKey();        if (k1 == k2 || (k1 != null && k1.equals(k2))) {            Object v1 = getValue();            Object v2 = e.getValue();            if (v1 == v2 || (v1 != null && v1.equals(v2)))                return true;        }        return false;    }    // 实现hashCode()    public final int hashCode() {        return (key==null   ? 0 : key.hashCode()) ^               (value==null ? 0 : value.hashCode());    }    public final String toString() {        return getKey() + "=" + getValue();    }    // 当向HashMap中添加元素时,绘调用recordAccess()。    // 这里不做任何处理    void recordAccess(HashMap<K,V> m) {    }    // 当从HashMap中删除元素时,绘调用recordRemoval()。    // 这里不做任何处理    void recordRemoval(HashMap<K,V> m) {    }}
静态内部类Entry就是一个单链表,所以HashMap采用拉链法避免冲突的。


3.HashMap在存储事采用了处余法

// 存储时:
int hash = key.hashCode(); // hashCode方法每个keyhash是一个固定的int
int index = hash % Entry[].length;
Entry[index] = value;

// 取值时:
int hash = key.hashCode();
int index = hash % Entry[].length;
return Entry[index];

4.当两个key的hashCode值相同时,有冲突发生但是不会覆盖数据,因为hashMap采用的链表的数组这种结构,用链地址法解决冲突。比如ABC的hashCode值相同(Entry中有next属性),它们的bucket都为0,那么当A先进入时,Entry[0]=A,B再加入时Entry[0]=B,B.next=A;C进入时Entry[0]=C,C.next=B,B.next=A。Entry[0]存储的是最后插入的元素。

public V put(K key, V value) {
        if (key == null)
            return putForNullKey(value); //null总是放在数组的第一个链表中,HashMap中允许有一个key=null的键值对
        int hash = hash(key.hashCode());
        int i = indexFor(hash, table.length);//bucket index
        //遍历链表
        for (Entry<K,V> e = table[i]; e != null; e = e.next) {
            Object k=e.key;
            //如果key在链表中已存在,则替换为新value
            if (e.hash == hash && (k== key || key.equals(k))) {//另数组下标相同不代表hashCode相同,判断是否相等hashCode和equals,(==,equals);
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }
        modCount++;
        addEntry(hash, key, value, i);//在第i个bucket处插入该键值对
        return null;

    }

 

void addEntry(int hash, K key, V value, int bucketIndex) {
    Entry<K,V> e = table[bucketIndex];
    table[bucketIndex] = new Entry<K,V>(hash, key, value, e); //参数e, 是Entry.next
    //如果size超过threshold,则扩充table大小。再散列
    if (size++ >= threshold)
            resize(2 * table.length);
}
 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=e.key;
            if (e.hash == hash && (k == key || key.equals(k)))
                return e.value;
        }
        return null;
}
key=null时键值对的存取
private V putForNullKey(V value) {//关键字为Null的键值对总存在bucket为0的链表中
        for (Entry<K,V> e = table[0]; e != null; e = e.next) {
            if (e.key == null) {
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }
        modCount++;
        addEntry(0, null, value, 0);
        return null;
    }
 
    private V getForNullKey() {
        for (Entry<K,V> e = table[0]; e != null; e = e.next) {
            if (e.key == null)
                return e.value;
        }
        return null;
    }
5.注意table初始大小并不是构造函数中的initialCapacity!!

而是 >= initialCapacity的2的n次幂!!!!

  public HashMap(int initialCapacity, float loadFactor) {
        .....
        // Find a power of 2 >= initialCapacity
        int capacity = 1;
        while (capacity < initialCapacity)
            capacity <<= 1;
        this.loadFactor = loadFactor;
        threshold = (int)(capacity * loadFactor);
        table = new Entry[capacity];
        init();
    }
6.再散列rehash过程

当哈希表的容量超过默认容量时,必须调整table的大小。当容量已经达到最大可能值时,那么该方法就将容量调整到Integer.MAX_VALUE返回,这时,需要创建一张新表,将原表的映射到新表中。

 void resize(int newCapacity) {
        Entry[] oldTable = table;
        int oldCapacity = oldTable.length;
        if (oldCapacity == MAXIMUM_CAPACITY) {
            threshold = Integer.MAX_VALUE;
            return;
        }
        Entry[] newTable = new Entry[newCapacity];
        transfer(newTable);
        table = newTable;
        threshold = (int)(newCapacity * loadFactor);

    }

 

    /**
     * Transfers all entries from current table to newTable.
     */
    void transfer(Entry[] newTable) {
        Entry[] src = table;
        int newCapacity = newTable.length;
        for (int j = 0; j < src.length; j++) {
            Entry<K,V> e = src[j];
            if (e != null) {
                src[j] = null;
                do {
                    Entry<K,V> next = e.next;
                    //重新计算index
                    int i = indexFor(e.hash, newCapacity);
                    e.next = newTable[i];
                    newTable[i] = e;
                    e = next;
                } while (e != null);
            }
        }

    }







0 0
原创粉丝点击