浏览 1041 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (11)
|
|
---|---|
作者 | 正文 |
发表时间:2012-02-23
http://www.hetaoblog.com/%E8%AF%B4%E4%B8%80%E8%AF%B4java%E9%87%8C%E9%9D%A2%E7%9A%84hashcode4-hashmap%E4%BB%A3%E7%A0%81%E5%88%86%E6%9E%901/
现在进入hashcode第4篇,HashMap代码分析, 先看到这个类的成员变量,有下面8个, static final int DEFAULT_INITIAL_CAPACITY = 16; // 默认的容量 static final int MAXIMUM_CAPACITY = 1 << 30; // 最大容量, 取到2^30 static final float DEFAULT_LOAD_FACTOR = 0.75f; // 默认的load factor, transient Entry[] table; // 这里,用一个数组存储了真正的Entry,Entry里面有成员k和v transient int size; // 大小 int threshold; // 容量限制 final float loadFactor; // 实际load factor transient volatile int modCount; 重点: * 实际的kv数据用一个entry类,存放在一个数组里面 下面看下默认的构造函数 public HashMap() { this.loadFactor = DEFAULT_LOAD_FACTOR; threshold = (int)(DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR); table = new Entry[DEFAULT_INITIAL_CAPACITY]; // 构造Entry表格 init(); // 这个函数是空的,可以忽略 } 下面是put函数,这段函数的主要意思: 1. 因为HashMap允许null(而HashTable不允许), 所以如果是null的话先固定放到第一个 2. 对对象的hashcode()再做一次hash,然后取索引,这里略有点故事,一般hash表的数组长度应该是质数,而jdk里面的HashMap选了2的指数作为数组大小,这里要做一次保护,这个下次可以单独写一点 3. 接下来,取到这个哈希数组里面的索引这项,然后对这项上的链表进行遍历,看看有没有存在一个key,和新放进来的元素hash值一样,并且(是同一个元素或者equals方法也返回相等)(这就是为什么Object.hashcode()里面会有那样的约定,如果你违法了那边的约定,假设两个对象equals返回true,本质上是同一个对象,但是hashcode不一样的话,那两次put会放到两个不同的地方了; 如果有的话,说明是同一个key对象,用新的value替换老的value,并且返回老的value 4. 如果不存在同一个key,那么就把该元素加入到这个哈希数组的这个位置(就是通过hashcode()再做hash,然后取索引的这项) 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 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; } 这里具体的addEntry代码如下: void addEntry(int hash, K key, V value, int bucketIndex) { Entry e = table[bucketIndex]; table[bucketIndex] = new Entry(hash, key, value, e); if (size++ >= threshold) resize(2 * table.length); } 以及Entry的实现和构造函数: static class Entry implements Map.Entry { final K key; V value; Entry next; final int hash; /** * Creates new entry. */ Entry(int h, K k, V v, Entry n) { value = v; next = n; key = k; hash = h; } 可以看到, 1. 新添加的元素被放到了哈希数组这个位置上面的链表的第一个; 2. 如果HashMap里面存放的总的元素个数超过了threshold(也就是capacity*loadfactor), 那么就把存放数据的数组扩大一倍 这里面是说总的元素个数,下面的代码是会打印2的。其实就是已经存了比较多的数据了,基本上直接hash一次访问到的概率比较小了,很可能很多元素都要继续通过访问后面的Entry链表来得到,效率就下降了,Hash的优势已经不那么明显了,所以把表格扩大一倍,把原来的元素根据hash值重新做一次索引和分布,尽可能让链表缩短,提高访问效率 Map m = new HashMap(); m.put("BB", 10); m.put("Aa", 11); System.out.println(m.size()); 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |