码迷,mamicode.com
首页 > 其他好文 > 详细

HashMap源码分析一

时间:2016-07-23 14:55:00      阅读:327      评论:0      收藏:0      [点我收藏+]

标签:

 
     HashMap在java编程中,算使用频率top10中的类了。这里是关于HashMap的源码的分析。一个类的源码分析,要看他的来龙去脉,他的历史迭代。一来从以前的版本开始分析,由易到难;二来可以看到他的迭代优化过程。HashMap的源码分析,就从很老以前的一个版本开始分析。
 
   简要说明,HashMap内部是一个数组,Object key 通过hash得到数组的index,如果数组index的位置有碰撞,则通过链表的形式接到那个位置上。取值是先hash到某个位置,然后在比较链表中每个值,获取到要取的值。
 
   这里先重点分析java1.2中HashMap的源码。
 
private transient Entry table[];
 
    private transient int count;
 
    private int threshold;
 
    private float loadFactor;
 
    private transient int modCount = 0;
 
 // 构造方法一
public HashMap(int initialCapacity, float loadFactor) {
    if (initialCapacity < 0)
        throw new IllegalArgumentException("Illegal Initial Capacity: "+
                                               initialCapacity);
        if (loadFactor <= 0)
            throw new IllegalArgumentException("Illegal Load factor: "+
                                               loadFactor);
        if (initialCapacity==0)
            initialCapacity = 1;
    this.loadFactor = loadFactor;
    table = new Entry[initialCapacity];
    threshold = (int)(initialCapacity * loadFactor);
    }
// 构造方法二
    public HashMap(int initialCapacity) {
    this(initialCapacity, 0.75f);
    }
// 构造方法三
    public HashMap() {
    this(101, 0.75f);
    }
// 构造方法四
    public HashMap(Map t) {
    this(Math.max(2*t.size(), 11), 0.75f);
    putAll(t);
    }

 

     属性数组table就是HashMap内部存放各种实体的数组。
     count是当前存放实体的数量。
     modCount是HashMap修改的次数,这里不是做什么统计用,在后面的HashIterator里要用到,HashIterator计算中,不希望HashMap被修改的。
     initialCapacity是HashMap内数组的初始长度。构造方法三中,默认初始长度为101,不太清楚这个101是怎么定义出来的。在构造方法四中,他的初始长度是11和入参map长度的2倍之间的大值。这里的11和101比较诡异。
     capacity是HashMap里数组的最大长度,随着HashMap里数组长度的变化而变化。
     threshold是用来提醒数组table该增大了的一个阀值。
     loadFactor是threshold值计算的一个系数,默认0.75,  threshold = capacity * loadFactor 。
 
基本的方法:
 
 // 查看HashMap存放数,count是HashMap的存放数属性
    public int size() {
       return count;
    }
 
    // 查看HashMap存放数是否为空,count==0表示存放数为空
    public boolean isEmpty() {
       return count == 0;
    }

 

 
对于调用方存入的一个个key和value,HashMap内部存放的是一个个内部实体对象,这里分析他的内部实体类。
 
//  HashMap内部实体类
    private static class Entry implements Map.Entry {
        int hash; // 存入HashMap key值的hashCode值
        Object key; // 存入HashMap的key值
        Object value; // 存入HashMap的value值
        Entry next; // 存入HashMap中下一个值,这里是为产生Hash碰撞后,链表结构准备的,单向链表
 
        // 构造方法
        Entry(int hash, Object key, Object value, Entry next) {
            this.hash = hash;
            this.key = key;
            this.value = value;
            this.next = next;
        }
 
        // 对象克隆方法
        protected Object clone() {
            return new Entry(hash, key, value,
                     (next==null ? null : (Entry)next.clone()));
        }
 
        public Object getKey() {
            return key;
        }
 
        public Object getValue() {
            return value;
        }
 
        // 设置value值,并返回老的value值
        public Object setValue(Object value) {
            Object oldValue = this.value;
            this.value = value;
            return oldValue;
        }
 
        // 判断实体对象的相等方法;只有在实体的key和value都相等时,才相等
        public boolean equals(Object o) {
            if (!(o instanceof Map.Entry))
            return false;
            Map.Entry e = (Map.Entry)o;
 
            return (key==null ? e.getKey()==null : key.equals(e.getKey())) &&
               (value==null ? e.getValue()==null : value.equals(e.getValue()));
        }
 
        // hashCode值,实体的hashCode值为key的hashCode和value的hashCode的异或值
        public int hashCode() {
            return hash ^ (value==null ? 0 : value.hashCode());
        }
 
        public String toString() {
            return key+"="+value;
        }
    }

 

 
HashMap同时也实现了Iterator接口,这里分析他的实现逻辑
 
// Iterator的类型 
    private static final int KEYS = 0; // Iterator对key进行操作
    private static final int VALUES = 1;// Iterator对value进行操作
    private static final int ENTRIES = 2;// iterator对entry进行操作
 
    // HashMap对Iterator接口的实现
    private class HashIterator implements Iterator {
        Entry[] table = HashMap.this.table; 
        int index = table.length;
        Entry entry = null; // 临时变量,存放hasNext方法时,遍历到的数组位链表对象
        Entry lastReturned = null; // 临时变量,存放next方法时,遍历到的对象;为remove方法中准备
        int type; // iterator的类型
 
        // 存放HashMap修改的次数
        private int expectedModCount = modCount;
 
        // 构造方法,入参为设定Iterator的类型
        HashIterator(int type) {
            this.type = type;
        }
 
        // 判断容器内是否有值
        public boolean hasNext() {
            while (entry==null && index>0)
            entry = table[--index];
 
            return entry != null;
        }
 
        // 获取下一个值
        public Object next() {
            // 遍历过程中,HashMap是不允许有改动,否则抛出异常ConcurrentModificationException
            if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
            // 遍历过程中,处理那种没有先调用hasNext方法的情况
            while (entry==null && index>0)
            entry = table[--index];
            // 如果entry不为null,处理entry
            if (entry != null) {
            // entry存放在临时变量lastReturned
            Entry e = lastReturned = entry;
            // entry存放链表的下一个节点,为下一次调用next方法做准备
            entry = e.next;
            // 根据type的类型,返回不同的值
            return type == KEYS ? e.key : (type == VALUES ? e.value : e);
            }
            throw new NoSuchElementException();
        }
 
        // 移除实体对象
        public void remove() {
            // remove调用之前,需要先调用hasNext方法,否则lastReturned=null,抛出异常
            if (lastReturned == null)
              throw new IllegalStateException();
            // 移除操作,HashMap也不希望在其他地方有修改
            if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
 
            Entry[] tab = HashMap.this.table;
            // 找到lastReturned对应数组的位置
            int index = (lastReturned.hash & 0x7FFFFFFF) % tab.length;
 
            // 找到对应数组的位置后,删除对应链表中的lastReturned值,下面是链表的操作
            for (Entry e = tab[index], prev = null; e != null;
              prev = e, e = e.next) {
                if (e == lastReturned) {
                    modCount++;
                    expectedModCount++;
                    if (prev == null)
                    tab[index] = e.next;
                    else
                    prev.next = e.next;
                    count--;
                    lastReturned = null;
                    return;
                }
            }
            throw new ConcurrentModificationException();
        }
    }

 

 
下面主要分析HashMap的添删查
 
// 判断HashMap是否包含key值
    public boolean containsKey(Object key) {
        Entry tab[] = table;
 
        // key值不为null
        if (key != null) {
            // 通过key计算出key对应数组table的下标,其实这里是可以封装成一个方法的,多个地方都要用到,后面jdk的版本都有改进
            int hash = key.hashCode();
            int index = (hash & 0x7FFFFFFF) % tab.length;
            // 查找链表,判断key是否存在
            for (Entry e = tab[index]; e != null; e = e.next)
                if (e.hash==hash && key.equals(e.key))
                    return true;
        } else { // key为null
            // 查找链表,判断key是否存在
            for (Entry e = tab[0]; e != null; e = e.next)
                if (e.key==null)
                    return true;
        }
 
        return false;
    }
 
    // 判断HashMap是否包含value值
    // 这个方法实际效率是很低的,需要先遍历数组,在一个个遍历链表,没有用到hash特性
    public boolean containsValue(Object value) {
        Entry tab[] = table;
        // value值为null
        if (value==null) {
            // 遍历数组
            for (int i = tab.length ; i-- > 0 ;)
            // 遍历链表
            for (Entry e = tab[i] ; e != null ; e = e.next)
                if (e.value==null)
                return true;
        } else {// value值不为null
            // 遍历数组
            for (int i = tab.length ; i-- > 0 ;)
            // 遍历链表
            for (Entry e = tab[i] ; e != null ; e = e.next)
                if (value.equals(e.value))
                return true;
        }
 
        return false;
    }
 
    // 获取key对应的value值
    public Object get(Object key) {
        Entry tab[] = table;
        // key不为null
        if (key != null) {
          // key的hashCode值
            int hash = key.hashCode();
          // key的hashCode计算出的hash值
            int index = (hash & 0x7FFFFFFF) % tab.length;
          // 通过index定位到数组,然后遍历对应的链表结构
            for (Entry e = tab[index]; e != null; e = e.next)
                if ((e.hash == hash) && key.equals(e.key))
                    return e.value;
        } else {
               // 如果key为null,直接遍历对应的链表结构
                for (Entry e = tab[0]; e != null; e = e.next)
                    if (e.key==null)
                        return e.value;
            }
 
        return null;
    }
 
    // 往HashMap中存放key和value 
    public Object put(Object key, Object value) {
 
        Entry tab[] = table;
        int hash = 0;
        int index = 0;
 
        // 这部分操作是当数组下标的对应位置已经有值,并且
        // key不为null
        if (key != null) {
            // 获得key对应数组的下标
            hash = key.hashCode();
            index = (hash & 0x7FFFFFFF) % tab.length;
            // 在数组对应的下标有值的情况下
            // key已经存在,把新的value代替老的value,并返回老的value
            for (Entry e = tab[index] ; e != null ; e = e.next) {
                if ((e.hash == hash) && key.equals(e.key)) {
                    Object old = e.value;
                    e.value = value;
                    return old;
                }
            }
        } else { // key为null,数组的下标是确定的,为0
            // 在数组对应的下标有值的情况下
            // key已经存在,把新的value代替老的value,并返回老的value
            for (Entry e = tab[0] ; e != null ; e = e.next) {
                if (e.key == null) {
                    Object old = e.value;
                    e.value = value;
                    return old;
                }
            }
        }
 
        // HashMap的修改次数加一
        modCount++;
        // 如果数组的使用长度大于或等于阀值threshold后
        if (count >= threshold) {
            // 扩展数组
            rehash();
 
            // 重新赋值tab
            tab = table;
            // 重新计算index
            index = (hash & 0x7FFFFFFF) % tab.length;
        }
 
        // 在原HashMap中,key没找到对应的value时
        // 这里先构造一个实体,把实体的next属性指向数组下标index对应的位置
        Entry e = new Entry(hash, key, value, tab[index]);
        // 数组下标对应的位置赋值实体e
        tab[index] = e;
        // 实体数量加一
        count++;
        return null;
    }
 
    // 推展数组
    private void rehash() {
        // 原数组长度
        int oldCapacity = table.length;
        // 原数组
        Entry oldMap[] = table;
 
        // 新数组长度,这里数组的增长策略是2倍加一的增
        int newCapacity = oldCapacity * 2 + 1;
        // 重新创建数组
        Entry newMap[] = new Entry[newCapacity];
 
        // HashMap的修改次数加一
        modCount++;
        // 更新HashMap的扩展数组的阀值
        threshold = (int)(newCapacity * loadFactor);
        // 赋值新数组到HashMap的数组
        // 这个地方存在并发问题,因为此时数组虽然创建了,但是实体值还是没有迁移过来的。
        // 如果此时有人读取value值,可能会返回为空
        table = newMap;
 
        // 把老数组里的值迁移到新数组里
        // 遍历数组
        for (int i = oldCapacity ; i-- > 0 ;) {
            // 遍历链表
            for (Entry old = oldMap[i] ; old != null ; ) {
                Entry e = old;
                old = old.next;
 
                int index = (e.hash & 0x7FFFFFFF) % newCapacity;
                e.next = newMap[index];
                newMap[index] = e;
            }
        }
    }
 
    // 移除实体
    public Object remove(Object key) {
        Entry tab[] = table;
        // key不为null
        if (key != null) {
            // 获得key对应的hash值和对应的数组下标
            int hash = key.hashCode();
            int index = (hash & 0x7FFFFFFF) % tab.length;
 
            // 根据对应的数组下标,找到链表对象
            // 遍历链表
            for (Entry e = tab[index], prev = null; e != null;
                 prev = e, e = e.next) {
                if ((e.hash == hash) && key.equals(e.key)) {
                    modCount++;
                    if (prev != null)
                        prev.next = e.next;
                    else
                        tab[index] = e.next;
 
                    count--;
                    Object oldValue = e.value;
                    e.value = null;
                    return oldValue;
                }
            }
        } else {// key为null,对应的数组下标为0
            // 根据对应的数组下标,找到链表对象
            // 遍历链表
            for (Entry e = tab[0], prev = null; e != null;
                 prev = e, e = e.next) {
                if (e.key == null) {
                    modCount++;
                    if (prev != null)
                        prev.next = e.next;
                    else
                        tab[0] = e.next;
 
                    count--;
                    Object oldValue = e.value;
                    e.value = null;
                    return oldValue;
                }
            }
        }
 
        return null;
    }
 
    // 批量添加
    public void putAll(Map t) {
        Iterator i = t.entrySet().iterator();
        while (i.hasNext()) {
            Map.Entry e = (Map.Entry) i.next();
            put(e.getKey(), e.getValue());
        }
    }
 
    // 清除数组内容
    public void clear() {
        Entry tab[] = table;
        modCount++;
        for (int index = tab.length; --index >= 0; )
            tab[index] = null;
        count = 0;
    } 

 

HashMap源码分析一

标签:

原文地址:http://www.cnblogs.com/sten/p/5698532.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!