JDK HashMap源代码分析_JAVA_编程开发_程序员俱乐部

中国优秀的程序员网站程序员频道CXYCLUB技术地图
热搜:
更多>>
 
您所在的位置: 程序员俱乐部 > 编程开发 > JAVA > JDK HashMap源代码分析

JDK HashMap源代码分析

 2010/12/25 0:04:29  wangchun8926  http://wangchun8926.javaeye.com  我要评论(0)
  • 摘要:今天来分析一下JDK中的HashMap类的源代码.HashMap不同于list、set,它又称为关联数组,它会同时存储一对值,让其之间形成一种关联.publicclassHashMap<K,V>extendsAbstractMap<K,V>implementsMap<K,V>,Cloneable,Serializable可以看到HashMap实现了Map接口,Map是集合类中的另外一个接口,它规定了所有map的实现类只能存储键值对publicHashMap(
  • 标签:has Map Hash jdk 代码 分析

今天来分析一下JDK中的HashMap类的源代码.

HashMap不同于list、set,它又称为关联数组,它会同时存储一对值,让其之间形成一种关联.

?

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

?

?可以看到HashMap实现了Map接口,Map是集合类中的另外一个接口,它规定了所有map的实现类只能存储键值对

?

 public HashMap() {
        this.loadFactor = DEFAULT_LOAD_FACTOR;//初始化装载因子
        threshold = (int)(DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR);//初始化容量
        table = new Entry[DEFAULT_INITIAL_CAPACITY];//初始化entry数组,用来存放数据
        init();//调用init方法,该方法暂未实现
    }

?

?默认的构造方法会使用默认值来初始化装载因子、容量、以及底层的entry数组

?

 public HashMap(int initialCapacity, float loadFactor) {
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal initial capacity: " +
                                               initialCapacity);
        if (initialCapacity > MAXIMUM_CAPACITY)
            initialCapacity = MAXIMUM_CAPACITY;
        if (loadFactor <= 0 || Float.isNaN(loadFactor))
            throw new IllegalArgumentException("Illegal load factor: " +
                                               loadFactor);

        //capacity必须是小于初始化容量的最大2的冥值
        int capacity = 1;
        while (capacity < initialCapacity)
            capacity <<= 1;

        this.loadFactor = loadFactor;
        threshold = (int)(capacity * loadFactor);
        table = new Entry[capacity];
        init();
    }

?

?该构造方法传入了两个参数,分别是初始化容量、装载因子,其中初始化容量必须是2的冥值

?

下面分析一下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);
        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;
    }

?

?首先判断一下存放入的key是否为空,若为空,则调用putForNullKey方法:

?

 private V putForNullKey(V value) {
        for (Entry<K,V> e = table[0]; e != null; e = e.next) {// 循环遍历entry链
            if (e.key == null) {//如果key为空,则用带待加的value替换原来的value
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }
        modCount++;
        addEntry(0, null, value, 0);
        return null;
    }

?该方法是一个私有方法,首先取出数组中第0个位置上的entry,而此时可以看到循环条件中有一个e = e.next的条件,明明是数组,那怎么会出现这样的条件呢?其实除了用entry数组存放数据外,在每个数组元素中,都有

一个指向下一个entry的引用.当key相同时.用新的value替代原来的value,最后构造一个entry添加进去entry链中,新添加的entry在整个链的末尾,他的下一个引用指向第0个索引上的entry.

接着分析put方法,上面是key为null时的处理,若key不为空,就根据key的hashcode取得一个hash值,然后计算出该hashcode对应的数组下标,若该下标对应的位置没有数据,则直接构造entry(通过addEntry方法)加入,否则 遍历entry链,

如果正在遍历的entry的key的hash值与待添加的key的hash值相等并且key值相等,则进行value替换.

?

?

 public V remove(Object key) {
        Entry<K,V> e = removeEntryForKey(key);
        return (e == null ? null : e.value);
    }

?

?remove方法用了删除entry,底层调用了removeEntryForKey方法:

?

 final Entry<K,V> removeEntryForKey(Object key) {
        int hash = (key == null) ? 0 : hash(key.hashCode());//根据key得到hash值
        int i = indexFor(hash, table.length);//根据hash值得到索引下标
        Entry<K,V> prev = table[i];//prev代表的i个元素
        Entry<K,V> e = prev;//e指向prev

        while (e != null) {
            Entry<K,V> next = e.next;//next代表下一个entry
            Object k;
            if (e.hash == hash &&
                ((k = e.key) == key || (key != null && key.equals(k)))) {//如果haah相等并且key值相等
                modCount++;
                size--;
                if (prev == e)//该链表中只剩有一个entry
                    table[i] = next;//让第i个索引上的entry直接指向next
                else
                    prev.next = next;//让前面的entry的下一个索引指向带删除entry的下一个索引
                e.recordRemoval(this);
                return e;
            }
            prev = e;//记录前面的entry
            e = next;//e指向下一个entry
        }

        return e;
    }

?该方法表示通过key删除entry

?

发表评论
用户名: 匿名