人物Q?/em>
王小胖:性别Q男。程序员Q工作经?/em>1 year。爱好:吃肉、电玩、马花。特技Q吃肉不用考虑胃的定w?/em>
马小花:性别Q女。学生,工作l验0 year。爱好:蛋糕、臭、王胖。特技Q能够降服王胖……
/**2011q?/em>2月,电媄《将爱情q行到底》火得不得了。周末,胖也陪着花ȝq部电媄。放映中Q小pq中的靖哥哥和杜拉拉感动的一沓糊涂,而小胖则心里暗自后悔没有C袋大爆米花来打发q无聊的旉。媄片结束,花已经是E涕一把泪一把,胖也只有装模作样地抽动了几下E子,一心只想着一会儿是吃麦当劌是必胜客?/em>*/
回到家中Q小胖和花各自玩着电脑?/em>
花Q胖子,你知?/em>Hashtable?/em>HashMap的区别吗Q?/em>
胖Q略知?/em>
花Q?#8230;…装什么!Q给我讲ԌQ!
胖Q好?#8230;…
W一个区别就先来说说l承关系吧?/strong>
如果你在baidu里google一下(技术类文章的搜索还是推荐googleQ,会发现网上的大致说法?#8220;׃Java发展的历史原因。Hashtable是基于陈旧的DictionarycȝQHashMap是Java 1.2引进的Map接口的一个实现?#8221;相同。这U说法没有错Q但是胖子觉得不够准,特别是对于我们这U大众菜鸟来_如果不去q的话Q可能就会造成一些理解上的差异。简单的认ؓHashtable没有l承Map接口。胖子之前就犯过q样的错误(胖子承认自己W,是真W?#8230;…Q??/p>
花Q那你怎么知道它们两个各自的承关pdQ胖子?/em>
我们可以参考一下最新的JDK1.6的源码,看看q两个类的定义:
public class Hashtable<K,V> extends Dictionary<K,V> implements Map<K,V>, Cloneable, java.io.Serializable {…} public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable {…}
可以看到hashtable也是l承了Map接口。它们的不同是HashtableQsince JDK1.0Q就l承了Dictionaryq个抽象c,而HashMapQsince JDK1.2Q承的则是AbstractMapq个抽象cR因为在Hashtable中看到承Map后所实现的方法是JDK1.2版本时加上去的,所以胖子猜惛_能是在JDK 1.2开发时Sun工程师出于统一的考虑使得Hashtable也承了Map接口?/p>
花Q哦Q原?/em>JDK源码q能看出来这个?/em>
胖Q?#8230;…后面q能看出更多东西的?/em>
花Q好期待啊?/em>
W二个区别我们从同步和ƈ发性上来说说它们两个的不同?/strong>
可以通过q两个类得源码来分析QHashtable中的主要Ҏ都做了同步处理,而HashMap则没有。可以说Hashtable在默认情冉|持同步,而HashMap在默认情况下是不支持的。我们在多线Eƈ发的环境下,可以直接使用HashtableQ但是要使用HashMap的话p自己增加同步处理了。对HashMap的同步处理可以用CollectionscL供的synchronizedMap静态方法;或者直接用JDK5.0之后提供的java.util.concurrent包里的ConcurrentHashMapcR?/p>
胖Q?/em>synchronizedMap静态方法和ConcurrentHashMapcL会以后再l你详细讲一下的。肥婆?/em>
花Q你保证啊。钥匙忘了你知道后果的?/em>
胖Q好?#8230;…
W三个区别就是它们对?/strong>null值的处理方式了?/strong>
我们依然能够从源代码中得知,Hashtable中,key和value都不允许出现null倹{?/p>
public synchronized V put(K key, V value) { // Make sure the value is not null if (value == null) { throw new NullPointerException(); } // Makes sure the key is not already in the hashtable. Entry tab[] = table; int hash = key.hashCode(); int index = (hash & 0x7FFFFFFF) % tab.length; //… }
在我们用上面的ҎӞ如参数value为nullQ可以从代码中直接看出程序会抛出NullPointerExceptionQ而在key为nullӞ则会?#8220;int hash = key.hashCode();“q段计算Hash值的q程中抛出NullPointerException?/span>而在在HashMap中,允许null作ؓkey存在Qƈ且和其他key的特性一Pq样的null值key只能有一个;另外HashMap允许多个value为null。这样大家就要注意了Q?nbsp;HashMap中就不能用get(key)Ҏ来判断HashMap中是否存在某个keyQ因为value为null和不存在该key的Entry都会q回null|而应该用containsKey()Ҏ来判断了?/p>
l果Q?/span> HashMap对于null值key的处理网上有?#8220;null 用new Object()来代替,其Entry.hashCode=0,而且在取出的时候还会还回null的?#8221;胖子我在d源码的过E中看到了null值的hash值确实是0 Q内部实现的数组中的index也是Q,但是能力有限没有看到转ؓnew Object()的过E?/p>
花Q?原来hashMap?/em>containsKeyq有q么个陷阱,以后肥婆要小心了?/em> W四个不同就是它们两?/strong>Hash值的获取方式了?/strong> q是通过源代码源代码QHashtable是直接用key对象的hash倹{?/p>
而HashMap则是利用key对象的hash值重新计一个新的hash倹{?/p>
花Q胖子,都用?/em>hash法Q你l我讲讲Hash法吧?/em> 胖Q嗯……以后的,今天我比较忙Q其实是不会Q?/em> 花Q你是不是不会啊Q嘿嘿(坏笑Q?/em> 胖Q什么不?#8230;…谈下一话题…… W五个不同就?/strong>Hashtable?/strong>HashMap它们两个内部实现方式的数l的初始大小和扩容的方式?/strong> HashMap中内部数l的初始定w?6Q?加蝲因子?.75Q而且数组定w增容后也要是2指数ơ幂Q?/p>
HashTable中的内部数组的初始容量是11Q加载因子也?.75数组的增Ҏ式ؓQoldCapacity * 2 + 1Q? W六个不同我们从它们两个遍历方式的内部实C来说?/strong> Hashtable HashMap都用了 Iterator。而由于历史原因,Hashtableq用了Enumeration的方??/p>
花Q?/em>Iterator?/em>Enumeration的区别是什么啊Q给我讲讌Ӏ?/em> 胖Q我不是说我没有旉嘛,下回的?/em> 花Q我都记下来Q省得你l我淯厅R(拿vW开始记账中Q?/em> 胖Q?#8230;…Q紧张) W七个不同时它们的拷贝构造函数的不同?/strong> 依然是通过查看源码Q可以发现它们两个对于拷贝函数初始容量的不同倹{?/p>
HashMap的实现是Q?/p>
而Hashtable的实现是Q?/p>
胖Q今天讲的已l很多了。我有点饿了Q肥婆?/em> 花Q看你今天的表现q么好。走Q带你去吃烤肉去?/em> 胖Q哈哈,肥婆万岁?/em> PS:下面打算写的一些东?/p>
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
public class TestCase {
public static void main(String[] args) {
Map<Integer,String> hashMap = new HashMap<Integer,String>();
hashMap.put(0, null);
hashMap.put(1, "one");
hashMap.put(2, "two");
hashMap.put(null, "null");
for(Entry<Integer, String> e : hashMap.entrySet()) {
System.out.println("Key: " + e.getKey() + " -- Value: " + e.getValue());
}
System.out.println(hashMap.get(0));
System.out.println(hashMap.get(4));
System.out.println("Contains key 0 ? :" + hashMap.containsKey(0));
System.out.println("Contains key 4 ? :" + hashMap.containsKey(4));
System.out.println("Contains value null ? :" + hashMap.containsValue(null));
}
}
Key: null -- Value: null
Key: 0 -- Value: null
Key: 1 -- Value: one
Key: 2 -- Value: two
null
null
Contains key 0 ? :true
Contains key 4 ? :false
Contains value null ? :true
public synchronized V put(K key, V value) {
// Make sure the value is not null
if (value == null) {
throw new NullPointerException();
}
// Makes sure the key is not already in the hashtable.
Entry tab[] = table;
int hash = key.hashCode();//hashcode
int index = (hash & 0x7FFFFFFF) % tab.length;
//…
}
public V put(K key, V value) {
if (key == null)
return putForNullKey(value);
int hash = hash(key.hashCode());//hashcode
int i = indexFor(hash, table.length);
//…
}
static int hash(int h) {
h ^= (h >>> 20) ^ (h >>> 12);
return h ^ (h >>> 7) ^ (h >>> 4);
}
/**
* The default initial capacity - MUST be a power of two.
*/
static final int DEFAULT_INITIAL_CAPACITY = 16;
/**
* The load factor used when none specified in constructor.
*/
static final float DEFAULT_LOAD_FACTOR = 0.75f;
public Hashtable() {
this(11, 0.75f);
}
protected void rehash() {
int oldCapacity = table.length;
Entry[] oldMap = table;
int newCapacity = oldCapacity * 2 + 1;
//…
}
public HashMap(Map<? extends K, ? extends V> m) {
this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1,
DEFAULT_INITIAL_CAPACITY), DEFAULT_LOAD_FACTOR);
putAllForCreate(m);
}
public Hashtable(Map<? extends K, ? extends V> t) {
this(Math.max(2*t.size(), 11), 0.75f);
putAll(t);
}