实现细节
修改操作
先来看下删除操作remove(key)。
Java代码- public V remove(Object key) {
- hash = hash(key.hashCode());
- return segmentFor(hash).remove(key, hash, null);
- }
整个操作是先定位到段,然后委托给段的remove操作。当多个删除操作并发进行时,只要它们所在的段不相同,它们就可以同时进行。下面是Segment的remove方法实现:
Java代码 <embed height="15" width="14" pluginspage="http://www.macromedia.com/go/getflashplayer" type="application/x-shockwave-flash" allowscriptaccess="always" quality="high" flashvars="clipboard=%20%20%20%20%20%20%20%20V%20remove(Object%20key%2C%20int%20hash%2C%20Object%20value)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20lock()%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20try%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20int%20c%20%3D%20count%20-%201%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20HashEntry%3CK%2CV%3E%5B%5D%20tab%20%3D%20table%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20int%20index%20%3D%20hash%20%26%20(tab.length%20-%201)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20HashEntry%3CK%2CV%3E%20first%20%3D%20tab%5Bindex%5D%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20HashEntry%3CK%2CV%3E%20e%20%3D%20first%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20while%20(e%20!%3D%20null%20%26%26%20(e.hash%20!%3D%20hash%20%7C%7C%20!key.equals(e.key)))%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20e%20%3D%20e.next%3B%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20V%20oldValue%20%3D%20null%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20(e%20!%3D%20null)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20V%20v%20%3D%20e.value%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20(value%20%3D%3D%20null%20%7C%7C%20value.equals(v))%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20oldValue%20%3D%20v%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2F%2F%20All%20entries%20following%20removed%20node%20can%20stay%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2F%2F%20in%20list%2C%20but%20all%20preceding%20ones%20need%20to%20be%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2F%2F%20cloned.%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2B%2BmodCount%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20HashEntry%3CK%2CV%3E%20newFirst%20%3D%20e.next%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20for%20(HashEntry%3CK%2CV%3E%20p%20%3D%20first%3B%20p%20!%3D%20e%3B%20p%20%3D%20p.next)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20newFirst%20%3D%20new%20HashEntry%3CK%2CV%3E(p.key%2C%20p.hash%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20newFirst%2C%20p.value)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20tab%5Bindex%5D%20%3D%20newFirst%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20count%20%3D%20c%3B%20%2F%2F%20write-volatile%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%20oldValue%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%20finally%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20unlock()%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%7D" src="http://www.javaeye.com/javascripts/syntaxhighlighter/clipboard_new.swf" lk_mediaid="lk_juiceapp_mediaPopup_1236652704298" lk_media="yes">- V remove(Object key, int hash, Object value) {
- lock();
- try {
- int c = count - 1;
- HashEntry<K,V>[] tab = table;
- int index = hash & (tab.length - 1);
- HashEntry<K,V> first = tab[index];
- HashEntry<K,V> e = first;
- while (e != null && (e.hash != hash || !key.equals(e.key)))
- e = e.next;
- V oldValue = null;
- if (e != null) {
- V v = e.value;
- if (value == null || value.equals(v)) {
- oldValue = v;
- // All entries following removed node can stay
- // in list, but all preceding ones need to be
- // cloned.
- ++modCount;
- HashEntry<K,V> newFirst = e.next;
- for (HashEntry<K,V> p = first; p != e; p = p.next)
- newFirst = new HashEntry<K,V>(p.key, p.hash,
- newFirst, p.value);
- tab[index] = newFirst;
- count = c; // write-volatile
- }
- }
- return oldValue;
- } finally {
- unlock();
- }
- }
整个操作是在持有段锁的情况下执行的,空白行之前的行主要是定位到要删除的节点e。接下来,如果不存在这个节点就直接返回null,否则就要将e前面的结点复制一遍,尾结点指向e的下一个结点。e后面的结点不需要复制,它们可以重用。下面是个示意图,我直接从这个网站 上复制的(画这样的图实在是太麻烦了,如果哪位有好的画图工具,可以推荐一下)。
删除元素之前:
删除元素3之后:
第二个图其实有点问题,复制的结点中应该是值为2的结点在前面,值为1的结点在后面,也就是刚好和原来结点顺序相反,还好这不影响我们的讨论。
整个remove实现并不复杂,但是需要注意如下几点。第一,当要删除的结点存在时,删除的最后一步操作要将count的值减一。这必须是最后一步 操作,否则读取操作可能看不到之前对段所做的结构性修改。第二,remove执行的开始就将table赋给一个局部变量tab,这是因为table是 volatile变量,读写volatile变量的开销很大。编译器也不能对volatile变量的读写做任何优化,直接多次访问非volatile实例 变量没有多大影响,编译器会做相应优化。
接下来看put操作,同样地put操作也是委托给段的put方法。下面是段的put方法:
Java代码 <embed height="15" width="14" pluginspage="http://www.macromedia.com/go/getflashplayer" type="application/x-shockwave-flash" allowscriptaccess="always" quality="high" flashvars="clipboard=%20%20%20%20%20%20%20%20V%20put(K%20key%2C%20int%20hash%2C%20V%20value%2C%20boolean%20onlyIfAbsent)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20lock()%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20try%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20int%20c%20%3D%20count%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20(c%2B%2B%20%3E%20threshold)%20%2F%2F%20ensure%20capacity%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20rehash()%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20HashEntry%3CK%2CV%3E%5B%5D%20tab%20%3D%20table%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20int%20index%20%3D%20hash%20%26%20(tab.length%20-%201)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20HashEntry%3CK%2CV%3E%20first%20%3D%20tab%5Bindex%5D%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20HashEntry%3CK%2CV%3E%20e%20%3D%20first%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20while%20(e%20!%3D%20null%20%26%26%20(e.hash%20!%3D%20hash%20%7C%7C%20!key.equals(e.key)))%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20e%20%3D%20e.next%3B%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20V%20oldValue%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20(e%20!%3D%20null)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20oldValue%20%3D%20e.value%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20(!onlyIfAbsent)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20e.value%20%3D%20value%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20else%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20oldValue%20%3D%20null%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2B%2BmodCount%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20tab%5Bindex%5D%20%3D%20new%20HashEntry%3CK%2CV%3E(key%2C%20hash%2C%20first%2C%20value)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20count%20%3D%20c%3B%20%2F%2F%20write-volatile%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%20oldValue%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%20finally%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20unlock()%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%7D" src="http://www.javaeye.com/javascripts/syntaxhighlighter/clipboard_new.swf" lk_mediaid="lk_juiceapp_mediaPopup_1236652704304" lk_media="yes">- V put(K key, int hash, V value, boolean onlyIfAbsent) {
- lock();
- try {
- int c = count;
- if (c++ > threshold) // ensure capacity
- rehash();
- HashEntry<K,V>[] tab = table;
- int index = hash & (tab.length - 1);
- HashEntry<K,V> first = tab[index];
- HashEntry<K,V> e = first;
- while (e != null && (e.hash != hash || !key.equals(e.key)))
- e = e.next;
- V oldValue;
- if (e != null) {
- oldValue = e.value;
- if (!onlyIfAbsent)
- e.value = value;
- }
- else {
- oldValue = null;
- ++modCount;
- tab[index] = new HashEntry<K,V>(key, hash, first, value);
- count = c; // write-volatile
- }
- return oldValue;
- } finally {
- unlock();
- }
- }