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

HashMap源码分析二

时间:2016-07-23 20:58:18      阅读:178      评论:0      收藏:0      [点我收藏+]

标签:

jdk1.2中HashMap的源码和jdk1.3中HashMap的源码基本上没变。在上篇中,我纠结的那个11和101的问题,在这边中找到答案了。
 
jdk1.2
 
    
public HashMap() {
    this(101, 0.75f);
    }
 
    public HashMap(Map t) {
    this(Math.max(2*t.size(), 11), 0.75f);
    putAll(t);
    }

 

 
jdk1.3
 
 public HashMap() {
        this(11, 0.75f);
    }
 
    public HashMap(Map t) {
        this(Math.max(2*t.size(), 11), 0.75f);
        putAll(t);
    }

 

 
原来jdk1.2中的101是开发者的一个误会 ?? 
 
HashMap源码在jdk1.4中,相比于jdk1.2、jdk1.3,有些优化性的改动,更成熟,考虑更完善了,一些java的编程风格也慢慢形成了。
 
static final int DEFAULT_INITIAL_CAPACITY = 16;
 
static final int MAXIMUM_CAPACITY = 1 << 30;
 
static final float DEFAULT_LOAD_FACTOR = 0.75f;

 

 
定义了三个静态常量。之前这些都是直接分散到代码中或者没有做考虑的。
DEFAULT_INITIAL_CAPACITY 是HashMap内数组的默认初始长度,不是以前的11了,现在确定为16。也不知道以前的11,现在的16是怎么定下来的。
MAXIMUM_CAPACITY 是HashMap内数组的最大长度,1 << 30 , 往下能看到数组的增长是每次加2倍,1 << 30 是int里成倍加的最大值,在加就超过int最大值了。
DEFAULT_LOAD_FACTOR 数组增长提示阀值的计算系数,之前是分散在代码里的
 
构造方法也有做修改
 
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);
 
        // 数组的初始长度,必须是大于1 << 4 小于 1 << 30 中的一个数,并且是通过1左移多少得来的,左移的位数在4到30之间。
        int capacity = 1;
        while (capacity < initialCapacity) 
            capacity <<= 1;
 
        this.loadFactor = loadFactor;
        threshold = (int)(capacity * loadFactor);
        table = new Entry[capacity];
        init(); // 钩子函数
    }
 

 

并且在全部的构造方法里,都藏有钩子函数 init() ; 内部实现为空实现。
 
/**
     * Initialization hook for subclasses. This method is called
     * in all constructors and pseudo-constructors (clone, readObject)
     * after HashMap has been initialized but before any entries have
     * been inserted.  (In the absence of this method, readObject would
     * require explicit knowledge of subclasses.)
     */
    void init() {
    }

 

 
这里对key和value为null的情况,做了些装饰
   
static final Object NULL_KEY = new Object();
 
    static Object maskNull(Object key) {
        return (key == null ? NULL_KEY : key);
    }
 
    static Object unmaskNull(Object key) {
        return (key == NULL_KEY ? null : key);
    }

 

 
一些工具方法
 
// 在jdk1.4中,计算对象hash值,抽离出一个公用方法了,同时,计算hash值的方式也有改变,后面会专门分析这个
   
static int hash(Object x) {
        int h = x.hashCode();
 
        h += ~(h << 9);
        h ^=  (h >>> 14);
        h +=  (h << 4);
        h ^=  (h >>> 10);
        return h;
    }
 
    // 比较两个对象的是否相同,也是一个抽离相同代码的过程
    static boolean eq(Object x, Object y) {
        return x == y || x.equals(y);
    }
 
    // 通过hash值,计算对应的数组下标,也是抽离相同代码的过程。
    // 之前构造中确定了length为 1 << 4 到 1 << 30 之间的数,则 h & (length -1),相当于截取h值的后多少位的值
    // 如果length为 1 << n  ,n为4到30,这相当于截取h的后n位的值    
    static int indexFor(int h, int length) {
        return h & (length-1);
    }

 

 
这里在分析下jdk1.4中的添改查
 
 // 判断HashMap中value是否存在
    public boolean containsValue(Object value) {
        // 当value为null,判断是否存在
        // 这里把判断是否存在value=null抽出了一个公用方法;特地全文搜索了下,现在只有这个地方用到了,可能当时有其他考虑
        if (value == null) 
            return containsNullValue();
 
        Entry tab[] = table;
        // 遍历数组
        // 这种遍历方式,相比以前的”for (int i = tab.length ; i-- > 0 ;)“,更习惯些
        for (int i = 0; i < tab.length ; i++)
            // 遍历链表
            for (Entry e = tab[i] ; e != null ; e = e.next)
                if (value.equals(e.value))
                    return true;
        return false;
    }
 
    // 判断是否存在value=null的实体
    private boolean containsNullValue() {
        Entry tab[] = table;
        for (int i = 0; i < tab.length ; i++)
            for (Entry e = tab[i] ; e != null ; e = e.next)
                if (e.value == null)
                    return true;
        return false;
    }
 
    // 判断key是否存在
    public boolean containsKey(Object key) {
        // 装饰key
        Object k = maskNull(key);
        // 计算key的hash值
        int hash = hash(k);
        // 计算key对应的数组下标值,这里把hash值的计算和数组下标的计算分别单独出一个方法,看着很清晰
        int i = indexFor(hash, table.length);
        Entry e = table[i]; 
        // 循环遍历链表
        while (e != null) {
            if (e.hash == hash && eq(k, e.key)) 
                return true;
            e = e.next;
        }
        return false;
    }
 
    // 获得key对应的值
    public Object get(Object key) {
        // 装饰key
        Object k = maskNull(key);
        // 计算key的hash值
        int hash = hash(k);
        // 计算key对应的数组下标值
        int i = indexFor(hash, table.length);
        Entry e = table[i]; 
        // 循环遍历链表
        while (true) {
            if (e == null)
                return e;
            if (e.hash == hash && eq(k, e.key)) 
                return e.value;
            e = e.next;
        }
    }
 
    // 获得key对应的实体,包括key和value等信息,同get方法类似
    Entry getEntry(Object key) {
        Object k = maskNull(key);
        int hash = hash(k);
        int i = indexFor(hash, table.length);
        Entry e = table[i]; 
        while (e != null && !(e.hash == hash && eq(k, e.key)))
            e = e.next;
        return e;
    }
 
    // 存储key和value值
    public Object put(Object key, Object value) {
        Object k = maskNull(key);
        int hash = hash(k);
        int i = indexFor(hash, table.length);
        // 这里把key做了装饰,就不需要像以前的版本一样,key分为null和不为null两种情况来处理
        // key对应的value值存在时的添加替换处理
        for (Entry e = table[i]; e != null; e = e.next) {
            if (e.hash == hash && eq(k, e.key)) {
                Object oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }
 
        modCount++;
        // key对应的value值不存在时的添加处理
        addEntry(hash, k, value, i);
        return null;
    }
 
    // 添加实体
    void addEntry(int hash, Object key, Object value, int bucketIndex) {
        table[bucketIndex] = new Entry(hash, key, value, table[bucketIndex]);
        // 如果已经超过阀值,这扩展数组,这里是在原基础2倍的扩展
        if (size++ >= threshold) 
            resize(2 * table.length);
    }
 
    // 只是添加key和value,不考虑数组的扩展和老value的返回
    private void putForCreate(Object key, Object value) {
        Object k = maskNull(key);
        int hash = hash(k);
        int i = indexFor(hash, table.length);
 
 
        for (Entry e = table[i]; e != null; e = e.next) {
            if (e.hash == hash && eq(k, e.key)) {
                e.value = value;
                return;
            }
        }
 
        createEntry(hash, k, value, i);
    }
 
    // 只是添加key和value,不考虑数组的扩展和老value的返回
    void createEntry(int hash, Object key, Object value, int bucketIndex) {
        table[bucketIndex] = new Entry(hash, key, value, table[bucketIndex]);
        size++;
    }
 
    // 批量只是添加key和value,不考虑数组的扩展和老value的返回
    // 这里的使用场景在构造传入一个map时
    void putAllForCreate(Map m) {
        for (Iterator i = m.entrySet().iterator(); i.hasNext(); ) {
            Map.Entry e = (Map.Entry) i.next();
            putForCreate(e.getKey(), e.getValue());
        }
    }
 
    // 扩展数组
    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);
        // 在jdk1.4里,有个改动,老表到新表,迁移完了之后在赋给HashMap使用
        // 解决了之前在迁移过程中有可能取不到数据的bug
        table = newTable;
        threshold = (int)(newCapacity * loadFactor);
    }
 
    // 数组从老表到新表
    void transfer(Entry[] newTable) {
        Entry[] src = table;
        int newCapacity = newTable.length;
        // 遍历数组
        for (int j = 0; j < src.length; j++) {
            Entry e = src[j];
            // 遍历链表,这版本中,很多地方把for改用成了while(while  或者 do while)
            // 其实后面jdk用推崇 for(;;),这都是向着效率越来越高,代码越来越简洁上优化
            if (e != null) {
                src[j] = null;
                do {
                    Entry next = e.next;
                    int i = indexFor(e.hash, newCapacity);  
                    e.next = newTable[i];
                    newTable[i] = e;
                    e = next;
                } while (e != null);
            }
        }
    }

 

 
idk中还有个小优化点,以前HashMap中获取不同类型的HashIterator,需要用户在构造中传入类型,现在只要调用不同的方法,他们分别做重载,挺有意思的,对开发者来说,更友好了,可以学习下
private class ValueIterator extends HashIterator {
        public Object next() {
            return nextEntry().value;
        }
    }
 
    private class KeyIterator extends HashIterator {
        public Object next() {
            return nextEntry().getKey();
        }
    }
 
    private class EntryIterator extends HashIterator {
        public Object next() {
            return nextEntry();
        }
    }
 
    // Subclass overrides these to alter behavior of views‘ iterator() method
    Iterator newKeyIterator()   {
        return new KeyIterator();
    }
    Iterator newValueIterator()   {
        return new ValueIterator();
    }
    Iterator newEntryIterator()   {
        return new EntryIterator();
    }

 

  
 

HashMap源码分析二

标签:

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

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