精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2009-04-15
最后修改:2009-04-15
1、 散列表要解决的一个问题就是散列值的冲突问题,通常是两种方法:链表法和开放地址法。链表法就是将相同hash值的对象组织成一个链表放在hash值对应的槽位;开放地址法是通过一个探测算法,当某个槽位已经被占据的情况下继续查找下一个可以使用的槽位。java.util.HashMap采用的链表法的方式,链表是单向链表,因此在删除过程中要自己维持prev节点,我想不采用双向链表是从节省空间考虑。一个典型的查找过程: for (Entry<K,V> e = table[indexFor(hash, table.length)]; e != null; e = e.next) { Object k; if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) return e; } HashMap采用链表法而不是开放地址法,猜想可能的原因是从实用角度出发,对空间和时间效率做出的折中选择。采用开放地址法,无论是线性探测或者二次探测都可能造成群集现象,而双重散列会要求散列表的装填程度比较低的情况下会有比较好的查找效率,容易造成空间的浪费。 threshold = (int)(capacity * loadFactor);
结合上面的负载因子的定义公式可知,threshold就是在此loadFactor和capacity对应下允许的最大元素数目,超过这个数目就重新resize,以降低实际的负载因子。默认的的负载因子0.75是对空间和时间效率的一个平衡选择。注意到的一点是resize的规模是现有capacity的两倍: if (size++ >= threshold) resize(2 * table.length); static int hash(int h) { h ^= (h >>> 20) ^ (h >>> 12); return h ^ (h >>> 7) ^ (h >>> 4); }
这个处理的原因在于HashMap的容量总是采用2的p次幂,而取index(槽位)的方法是 static int indexFor(int h, int length) { return h & (length-1); }
这一运算等价于对length取模,也就是 Iterator() { expectedModCount = modCount; if (size > 0) { // advance to first entry Entry[] t = table; while (index < t.length && (next = t[index++]) == null) ; } } 在迭代过程中,判断modCount跟expectedModCount是否相等,如果不相等就表示已经有其他线程修改了map final Entry<K,V> nextEntry() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); 注意到modCount声明为volatile,保证线程之间修改的可见性。 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2009-04-15
请教
如果想空间的利用更充分,是不是要写一个返回结果更平均的hashCode()方法?怎么写一个好的hashCode()方法呢? |
|
返回顶楼 | |
发表时间:2009-12-04
两个问题;第一,上面的hash函数的参数是h,请问最原始的h是怎么得到的?
第二,根据0.75的threshold,是不是真实情况下散列总是装不满的?也是为了下挂的链表尽量的小? |
|
返回顶楼 | |
发表时间:2009-12-04
piper 写道 两个问题;第一,上面的hash函数的参数是h,请问最原始的h是怎么得到的?
第二,根据0.75的threshold,是不是真实情况下散列总是装不满的?也是为了下挂的链表尽量的小? 1、h就是你对象的hashCode方法得到的 2、是的 |
|
返回顶楼 | |
浏览 8736 次