论坛首页 Java企业应用论坛

HashMap深度分析

浏览 52212 次
该帖已经被评为精华帖
作者 正文
   发表时间:2011-02-18   最后修改:2011-02-18
在方法中:
void transfer(Entry[] newTable) {
        Entry[] src = table;
        int newCapacity = newTable.length;
        for (int j = 0; j < src.length; j++) {
            Entry<K,V> e = src[j];
            if (e != null) {
                src[j] = null;
                do {
                    Entry<K,V> next = e.next;
                    int i = indexFor(e.hash, newCapacity);  
                    e.next = newTable[i];
                    newTable[i] = e;
                    e = next;
                } while (e != null);
            }
        }
    }


由于 使用了新的newCapacity,indexFor出来的值 就不会一样,这一句
e.next = newTable[i]; 为什么不写成 e.next = null;

希望牛人回答

---
呵呵  想明白了 
0 请登录后投票
   发表时间:2011-02-18  
又被翻出来了,不过我还是仔细看了一把
个人感觉下面引用里面提到的死循环不是那么回事吧,就纯粹的resize来讲,e1和本身不会是个闭环...
el的next应该是null(就引用里面的例子...)
闭环应该是在多线程同时扩容的时候可能产生,因为扩容的时候会把entry链表反序...
Refer:http://pt.alibaba-inc.com/wp/dev_related_969/hashmap-result-in-improper-use-cpu-100-of-the-problem-investigated.html
ch_space 写道

死锁是扩容操作与put或get方法并发引起的,看源码:
引用

void transfer(Entry[] newTable) {
        Entry[] src = table;
        int newCapacity = newTable.length;
        for (int j = 0; j < src.length; j++) {
            Entry<K,V> e = src[j];
            if (e != null) {
                src[j] = null;
                do {
                    Entry<K,V> next = e.next;
                    int i = indexFor(e.hash, newCapacity);
                    e.next = newTable[i];
                    newTable[i] = e;
//切换2
                    e = next;
                } while (e != null);
            }
        }
    }



//get()
for (Entry<K,V> e = table[indexFor(hash, table.length)];//切换1
             e != null;
             e = e.next) 


table初始状态:

假设执行get的线程1在e = table[indexFor(hash, table.length)]后进行“切换1”,切换到线程2的put方法,而且进行了扩容。此时table和newTable的状态是:

线程2在newTable[i] = e后进行“切换2”,继续执行线程1的get,在for循环里e.next==e,永远不为空,若
if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
不满足,则进入了死循环。

0 请登录后投票
   发表时间:2011-04-10  
楼主威武,受教
0 请登录后投票
   发表时间:2011-04-11  
HashMap 是个好东西。。常用
0 请登录后投票
   发表时间:2011-07-12  
zhang34082 写道
在方法中:
void transfer(Entry[] newTable) {
        Entry[] src = table;
        int newCapacity = newTable.length;
        for (int j = 0; j < src.length; j++) {
            Entry<K,V> e = src[j];
            if (e != null) {
                src[j] = null;
                do {
                    Entry<K,V> next = e.next;
                    int i = indexFor(e.hash, newCapacity);  
                    e.next = newTable[i];
                    newTable[i] = e;
                    e = next;
                } while (e != null);
            }
        }
    }


由于 使用了新的newCapacity,indexFor出来的值 就不会一样,这一句
e.next = newTable[i]; 为什么不写成 e.next = null;

希望牛人回答

---
呵呵  想明白了 


我也有这个疑问。。。什么原因?
0 请登录后投票
   发表时间:2011-07-12  
我承认我没看懂 先自己研究一下,再看这里的分析
0 请登录后投票
   发表时间:2011-07-18   最后修改:2011-07-18
   static int indexFor(int h, int length) {
        return h &amp; (length-1);
    }





“h &
(length-1)”
其实这里是很有讲究的,为什么是和(length-1)进行按位与运算呢?这样做是为了提高HashMap的效率。什么?这样能提高效率?且听我细细道来。

首先我们要确定一下,HashMap的数组长度永远都是偶数,即使你在初始化的时候是这样的new HashMap(15,0.75);因为在构造函数内部,上面也讲过,有这样的一段代码:

while (capacity  e = table[i]; e != null; e = e.next) {
            Object k;
            if (e.hash == hash &amp;&amp; ((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;
    }



jdk哪有这么神秘?真以为每行代码都是高效率的啊?
这操作只是理所当然的操作,你自己去实现,一样会加这行代码:求余而已~不求余显然会出问题的,即使是奇数,一样要求余的~而且在均匀分布来讲,也是一样的,取决于hash函数而已

hashmap有这么高深吗?不就一个hash函数吗
0 请登录后投票
   发表时间:2011-07-18   最后修改:2011-07-18
zhang34082 写道
   static int indexFor(int h, int length) {
        return h &amp; (length-1);
    }





“h &
(length-1)”
其实这里是很有讲究的,为什么是和(length-1)进行按位与运算呢?这样做是为了提高HashMap的效率。什么?这样能提高效率?且听我细细道来。

首先我们要确定一下,HashMap的数组长度永远都是偶数,即使你在初始化的时候是这样的new HashMap(15,0.75);因为在构造函数内部,上面也讲过,有这样的一段代码:

while (capacity  e = table[i]; e != null; e = e.next) {
            Object k;
            if (e.hash == hash &amp;&amp; ((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;
    }



jdk哪有这么神秘?真以为每行代码都是高效率的啊?
这操作只是理所当然的操作,你自己去实现,一样会加这行代码:求余而已~不求余显然会出问题的,即使是奇数,一样要求余的~而且在均匀分布来讲,也是一样的,取决于hash函数而已

hashmap有这么高深吗?不就一个hash函数吗

0 请登录后投票
   发表时间:2011-07-18  
snake1987 写道
zhang34082 写道
   static int indexFor(int h, int length) {
        return h &amp; (length-1);
    }





“h &
(length-1)”
其实这里是很有讲究的,为什么是和(length-1)进行按位与运算呢?这样做是为了提高HashMap的效率。什么?这样能提高效率?且听我细细道来。

首先我们要确定一下,HashMap的数组长度永远都是偶数,即使你在初始化的时候是这样的new HashMap(15,0.75);因为在构造函数内部,上面也讲过,有这样的一段代码:

while (capacity  e = table[i]; e != null; e = e.next) {
            Object k;
            if (e.hash == hash &amp;&amp; ((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;
    }



jdk哪有这么神秘?真以为每行代码都是高效率的啊?
这操作只是理所当然的操作,你自己去实现,一样会加这行代码:求余而已~不求余显然会出问题的,即使是奇数,一样要求余的~而且在均匀分布来讲,也是一样的,取决于hash函数而已

hashmap有这么高深吗?不就一个hash函数吗


的确,分布的均匀与否,跟与运算没关系,主要还是hash计算
0 请登录后投票
   发表时间:2011-07-18  
hash算法分布均匀,同时要保证容量的选择能够保证hash的均匀性,二者缺一不可
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics