1)HashTable是同步的,HashMap是非同步的
HashTable中put和get方法:
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;
@SuppressWarnings("unchecked")
Entry<K,V> entry = (Entry<K,V>)tab[index];
for(; entry != null ; entry = entry.next) {
if ((entry.hash == hash) && entry.key.equals(key)) {
V old = entry.value;
entry.value = value;
return old;
}
}
addEntry(hash, key, value, index);
return null;
}
public synchronized V get(Object key) {
Entry<?,?> tab[] = table;
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.length;
for (Entry<?,?> e = tab[index] ; e != null ; e = e.next) {
if ((e.hash == hash) && e.key.equals(key)) {
return (V)e.value;
}
}
return null;
}
HashMap中put和get方法:
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
public V get(Object key) {
Node<K,V> e;
return (e = getNode(hash(key), key)) == null ? null : e.value;
}
2)HashTable与HashMap实现的接口一致,但HashTable继承自Dictionary,而HashMap继承自AbstractMap;
3)HashTable不允许null值(key和value都不可以) ,HashMap允许null值(key和value都可以)。
HashMap put:
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = reshize()).length;
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
else {
Node<K,V> e; K k;
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
else if (p instanceof TreeNode)
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else {
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;
}
}
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
return oldValue;
}
}
++modCount;
if (++size > threshold)
resize();
afterNodeInsertion(evict);
return null;
}
4)HashTable有一个contains(Object value)功能和containsValue(Object value)功能一样。
HashTable中的contains方法在HashMap中就被取消了, 那么我们来具体看下HashTable中的contains方法的作用:
public synchronized boolean contains(Object value) {
if (value == null) {
throw new NullPointerException();
}
Entry<?,?> tab[] = table;
for (int i = tab.length ; i-- > 0 ;) {
for (Entry<?,?> e = tab[i] ; e != null ; e = e.next) {
if (e.value.equals(value)) {
return true;
}
}
}
return false;
}
然后再看下HashTable中的containsValue方法:
public boolean containsValue(Object value) {
return contains(value);
}
这里就很明显了, contains方法其实做的事情就是containsValue, 里面将value值使用equals进行对比, 所以在HashMap中直接取消了contains方法而是使用containsValue代替.
5)HashTable使用Enumeration进行遍历,HashMap使用Iterator进行遍历。
废弃的接口:Enumeration
Enumeration接口是JDK1.0时推出的,是最好的迭代输出接口,最早使用Vector(现在推荐使用ArrayList)时就是使用Enumeration接口进行输出。虽然Enumeration是一个旧的类,但是在JDK1.5之后为Enumeration类进行了扩充,增加了泛型的操作应用。
Enumeration接口常用的方法有hasMoreElements()(判断是否有下一个值)和 nextElement()(取出当前元素),这些方法的功能跟Iterator类似,只是Iterator中存在删除数据的方法,而此接口不存在删除操作。
为什么还要继续使用Enumeration接口
Enumeration和Iterator接口功能相似,而且Iterator的功能还比Enumeration多,那么为什么还要使用Enumeration?这是因为java的发展经历了很长时间,一些比较古老的系统或者类库中的方法还在使用Enumeration接口,因此为了兼容,还是需要使用Enumeration。
Hashtable 遍历方式:
//第一种遍历方式, 使用key
Enumeration<String> keys = ht.keys();
while (keys.hasMoreElements()) {
String key = (String) keys.nextElement();
System.out.println("HashTable 的 key 是 : " + key + ", Value 是 : "+ht.get(key));
}
//第二种方式:使用elements()
Enumeration<Person> elements = ht.elements();
while (elements.hasMoreElements()) {
Person person = (Person) elements.nextElement();
System.out.println(person);
}
//第三种方式:使用keySet()
Iterator<String> iterator = ht.keySet().iterator();
while (iterator.hasNext()) {
String key = (String) iterator.next();
Person value = hm.get(key);
System.out.println(key + " " + value);
}
//第四种方式:使用entrySet
Iterator<Entry<String, Person>> iterator2 = ht.entrySet().iterator();
while (iterator2.hasNext()) {
Map.Entry<String, Person> entry = (Map.Entry<String, Person>) iterator2.next();
String key = entry.getKey();
Person value = entry.getValue();
System.out.println(key + " " + value);
}
//HashMap
//第一种方式
Iterator<Entry<String, Person>> iterator3 = hm.entrySet().iterator();
while (iterator3.hasNext()) {
Map.Entry<String, Person> entry = (Map.Entry<String, Person>) iterator3.next();
String key = entry.getKey();
Person value = entry.getValue();
System.out.println(key + " " + value);
}
//第二种方式
Iterator<String> iterator4 = hm.keySet().iterator();
// the second method to travel the map
while (iterator4.hasNext()) {
String key = (String) iterator4.next();
Person value = hm.get(key);
System.out.println(key + " " + value);
}
6)HashTable中hash数组默认大小是11,增加的方式是 old*2+1。HashMap中hash数组的默认大小是16,而且一定是2的指数
7)哈希值的使用不同
HashTable:,HashTable直接使用对象的hashCode
HashMap:HashMap重新计算hash值,而且用与代替求模:
int hash = hash(k);
int i = indexFor(hash, table.length);
static int hash(Object x) {
h ^= (h >>> 20) ^ (h >>> 12);
return h ^ (h >>> 7) ^ (h >>> 4);
}
static int indexFor(int h, int length) {
return h & (length-1);
}
HashMap与HashSet的关系
1、HashSet底层是采用HashMap实现的:
public HashSet() {
map = new HashMap<E,Object>();
}
2、调用HashSet的add方法时,实际上是向HashMap中增加了一行(key-value对),该行的key就是向HashSet增加的那个对象,该行的value就是一个Object类型的常量。
private static final Object PRESENT = new Object(); public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
public boolean remove(Object o) {
return map.remove(o)==PRESENT;
}
HashMap 和 ConcurrentHashMap 的关系
关于这部分内容建议自己去翻翻源码,ConcurrentHashMap 也是一种线程安全的集合类,他和HashTable也是有区别的,主要区别就是加锁的粒度以及如何加锁,ConcurrentHashMap 的加锁粒度要比HashTable更细一点。将数据分成一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据的时候,其他段的数据也能被其他线程访问。