`
wangqisen
  • 浏览: 46980 次
文章分类
社区版块
存档分类
最新评论

【jdk源码解析三】java.util.Hashtable

 
阅读更多

HashMap是Hashtable的非线程安全版。非常明显由源码可以看出,Hashtable与HashMap并不出于同一个人之手,代码风格有很大差别。

首先,Hashtable继承自Dictionary接口而不是Map接口,为什么呢?Dictionary接口其实与Map接口差不多,但是已经被废弃,被Map接口所取代。与HashMap相同,Hashtable也实现了java.lang.Cloneable接口与java.io.Serializable接口。

数据结构上,Hashtable与HashMap的结构是几乎一样的实现。

那么Hashtable在源码大体结构与HashMap相似的情况下,有哪些区别呢?首先,Hashtable是线程安全的,其大多数方法是synchronized()的,当然构造方法除外(构造器不能用abstract,final,static,synchronized及native来修饰只能是public,protected,private)。

其次,Hashtable的初始默认桶容量为11,默认装载因子为0.75,而HashMap则是16和0.75。

然后,Hashtable的索引计算方法与HashMap完全不同,实际上,HashMap会使用一个非常好用的哈希函数来对键的hashCode做哈希,然后将其映射到Entry数组的对应位置,而Hashtable则将键值的hashCode直接与0x7fffffff直接相与,然后模数组的长度,这里就可以看出,由于Hashtable是早期版本,因此,并没有想出像HashMap那样,使用桶容量必须是2的幂次以及相应的求数组索引的方法的一系列优化措施,因此,这里的模运算不能不说是一个非常耗时的操作,相比HashMap的实现,就没有那么精妙。

public synchronized boolean containsKey(Object key) {
	Entry tab[] = table;
	int hash = key.hashCode();
	int index = (hash & 0x7FFFFFFF) % tab.length;
	for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) {
	    if ((e.hash == hash) && e.key.equals(key)) {
		return true;
	    }
	}
	return false;

然后是关于扩容。Hashtable采用的也是倍增的方式来实现的扩容,由于其没有桶容量必须是2的幂次的要求,因此,其扩容时实际上是采用2倍加1的方式来扩容。

  protected void rehash() {
	int oldCapacity = table.length;
	Entry[] oldMap = table;

	int newCapacity = oldCapacity * 2 + 1;
	Entry[] newMap = new Entry[newCapacity];

	modCount++;
	threshold = (int)(newCapacity * loadFactor);
	table = newMap;

	for (int i = oldCapacity ; i-- > 0 ;) {
	    for (Entry<K,V> old = oldMap[i] ; old != null ; ) {
		Entry<K,V> e = old;
		old = old.next;

		int index = (e.hash & 0x7FFFFFFF) % newCapacity;
		e.next = newMap[index];
		newMap[index] = e;
	    }
	}
    }
关于Hashtable,还有一点,Hashtable实现了map自己的hashCode。这个函数应该是神来之笔,因为其实现是很巧妙的。

public synchronized int hashCode() {
        /*
         * This code detects the recursion caused by computing the hash code
         * of a self-referential hash table and prevents the stack overflow
         * that would otherwise result.  This allows certain 1.1-era
         * applets with self-referential hash tables to work.  This code
         * abuses the loadFactor field to do double-duty as a hashCode
         * in progress flag, so as not to worsen the space performance.
         * A negative load factor indicates that hash code computation is
         * in progress.
         */
        int h = 0;
        if (count == 0 || loadFactor < 0)
            return h;  // Returns zero

        loadFactor = -loadFactor;  // Mark hashCode computation in progress
        Entry[] tab = table;
        for (int i = 0; i < tab.length; i++)
            for (Entry e = tab[i]; e != null; e = e.next)
                h += e.key.hashCode() ^ e.value.hashCode();
        loadFactor = -loadFactor;  // Mark hashCode computation complete

	return h;
    }

为什么会要用到loadFactor?每次计算为什么要先乘个-1?我没有想明白,但是在stackoverflow上问了这个问题之后,得到了大神的帮助。由于有可能键值对存在类似于<"key1",this>这样的方式,如果不加控制的话,会造成死循环的调用hashCode,因此,需要使用loadFactor作为标志,如果调用时小于0,说明进入了死循环,直接就返回0。神作啊!详情移步http://stackoverflow.com/questions/21085135/how-to-understand-the-hashcode-funtion-of-java-util-hashtable-in-the-source-code

另,HashMap中并没有看到对于hashCode的实现。

分享到:
评论

相关推荐

    javajdk源码学习-JavaSourceLearn:JDK源码学习

    jdk源码学习 JavaSourceLearn 版本号 版本 corretto-1.8.0_275 方式 逐步阅读源码添加注释、notes文件夹添加笔记 计划学习任务计划 标题为包名,后面序号为优先级1-4,优先级递减 java.lang Object 1 String 1 ...

    JDK 1.5的泛型實現(Generics in JDK 1.5)

    JDK 1.5的泛型實現(Generics in JDK 1.5) 1 侯捷觀點 JDK 1.5的泛型實現 ...Generics in JDK 1.5 — .......Java語言基礎,使用過 Java Collections。.......JDK1.5 ....本文程式源碼可至侯捷...java.util.ArrayList源碼 ...

    java jdk实列宝典 光盘源代码

    三维弹球,BouncingB.java; 贪吃蛇游戏SnakeModel.java; java的声音处理,介绍java中如何处理声音,包括实现响铃,播放wav,au等音频文件,以及控制声音的大小和音量,Beep.java; 媒体播放器,JMFMediaPlayer.java...

    Java 面试宝典

    1、一个".java"源文件中是否可以包括多个类(不是内部类)?有什么限制? ...... 7 2、Java 有没有 goto? .......................................................................................................

    JAVA核心知识点整理(有效)

    25 JAVA8 与元数据.................................................................................................................................25 2.4. 垃圾回收与算法 .................................

    java面试题

    37. 一个".java"源文件中是否可以包括多个类(不是内部类)?有什么限制? 18 38. 比较truncate和delete 命令 18 39. 解释$ORACLE_HOME 和$ORACLE_BASE的区别? 19 40. session与cookie的区别和联系? 19 41. ...

    JAVA面试题最全集

    1.Java有那些基本数据类型,String是不是基本数据类型,他们有何区别。 2.字符串的操作: 写一个方法,实现字符串的反转,如:输入abc,输出cba 写一个方法,实现字符串的替换,如:输入bbbwlirbbb,输出...

    java基础题 很全面

    53. 一个".java"源文件中是否可以包括多个类(不是内部类)?有什么限制? 13 54. java中有几种方法可以实现一个线程?用什么关键字修饰同步方法? stop()和suspend()方法为何不推荐使用? 13 55. java中有几种类型的流?...

    java面试宝典2012版.pdf

    1、一个".java"源文件中是否可以包括多个类(不是内部类)?有什么限制? 2、Java有没有goto? 3、说说&和&&的区别。 4、在JAVA中如何跳出当前的多重嵌套循环? 5、switch语句能否作用在byte上,能否作用在long上...

    jboss-as-7.1.1.Final

    import java.util.Hashtable; import javax.naming.Context; import javax.naming.InitialContext; import javax.naming.NamingException; import com.ejb.HelloWorldRemote; public class ClientTest { /** * @...

    Java面试宝典-经典

    2、编写一个程序,将d:\java目录下的所有.java文件复制到d:\jad目录下,并将原来文件的扩展名从.java改为.jad。 62 3、编写一个截取字符串的函数,输入为一个字符串和字节数,输出为按字节截取的字符串,但要保证...

    java 面试题 总结

    Hashtable继承自Dictionary类,而HashMap是Java1.2引进的Map interface的一个实现。 最大的不同是,Hashtable的方法是Synchronize的,而HashMap不是,在多个线程访问Hashtable时,不需要自己为它的方法实现同步,而...

    java面试题大全(2012版)

    2、编写一个程序,将d:\java目录下的所有.java文件复制到d:\jad目录下,并将原来文件的扩展名从.java改为.jad。 62 3、编写一个截取字符串的函数,输入为一个字符串和字节数,输出为按字节截取的字符串,但要保证...

    java面试宝典

    42、一个“.java”源文件中是否可以包含多个类(不是内部类)?有什么限制? 12 43、说出一些常用的类,包,接口,请各举5 个。 12 44、Anonymous Inner Class (匿名内部类) 是否可以extends(继承)其它类?是否可以...

    最新Java面试宝典pdf版

    2、编写一个程序,将d:\java目录下的所有.java文件复制到d:\jad目录下,并将原来文件的扩展名从.java改为.jad。 62 3、编写一个截取字符串的函数,输入为一个字符串和字节数,输出为按字节截取的字符串,但要保证...

    Java面试笔试资料大全

    2、编写一个程序,将d:\java目录下的所有.java文件复制到d:\jad目录下,并将原来文件的扩展名从.java改为.jad。 62 3、编写一个截取字符串的函数,输入为一个字符串和字节数,输出为按字节截取的字符串,但要保证...

    java面试宝典2012

    2、编写一个程序,将d:\java目录下的所有.java文件复制到d:\jad目录下,并将原来文件的扩展名从.java改为.jad。 68 3、编写一个截取字符串的函数,输入为一个字符串和字节数,输出为按字节截取的字符串,但要保证...

Global site tag (gtag.js) - Google Analytics