HashTable和HashMap的区别
HashTble和HashMap都实现了Map接口,但是它们也有很多区别
HashTble和HashMap的区别主要的以下几点:
1、HashTable是线程安全的,因为它的大部分方法使用了Sychronized进行修饰,所以它的方法能够进行同步,从而保证线程安全性,
HashMap的方法没有使用Sychronized修饰,所以不能保证线程的安全,
如果HashMap想要保证方法同步,可以使用语句Map map = Collections.sychronizedMap(hashmap),如果是JDK5以上,可以使用CuncurrentHashMap;
2、HashTable不能存储键值对为null的数据,而HashMap可以;
3、HashTble和HashMap内部计算hash值的方法不同;
4、HashTble使用Enumeration迭代器,HashMap使用Iterator迭代器,后者是一种fail-fash迭代器,这种迭代器当集合对象创建了Iterator或者ListIterator后,
再试图改变集合的话,会抛出ConcurrentModificationException异常;
5、HashTable中hash数组默认大小是11,增加的方式是old*2+1。HashMap中hash数组的默认大小是16,而且一定是2的指数
6、HashTable继承自Dictionary类,而HashMap继承自AbstractMap类;
HashMap为什么线程不安全
个人觉得HashMap在并发时可能出现的问题主要是两方面,首先如果多个线程同时使用put方法添加元素,而且假设正好存在两个put的key发生了碰撞(hash值一样),那么根据HashMap的实现,这两个key会添加到数组的同一个位置,这样最终就会发生其中一个线程的put的数据被覆盖。第二就是如果多个线程同时检测到元素个数超过数组大小*loadFactor,这样就会发生多个线程同时对Node数组进行扩容,都在重新计算元素位置以及复制数据,但是最终只有一个线程扩容后的数组会赋给table,也就是说其他线程的都会丢失,并且各自线程put的数据也丢失。
HashMap基于hashing原理,我们通过put()和get()方法储存和获取对象。当我们将键值对传递给put()方法时,它调用键对象的hashCode()方法来计算hashcode,让后找到bucket位置来储存值对象。当获取对象时,通过键对象的equals()方法找到正确的键值对,然后返回值对象。HashMap使用LinkedList来解决碰撞问题,当发生碰撞了,对象将会储存在LinkedList的下一个节点中。 HashMap在每个LinkedList节点中储存键值对对象。
当两个不同的键对象的hashcode相同时会发生什么? 它们会储存在同一个bucket位置的LinkedList中。键对象的equals()方法用来找到键值对
HashMap与HashTable的主要区别:
HashMap和HashTable存在很多的相同点,但是也有几个重要的不同点
-
1、我们分别查看源码定义可以看出,Hashtable继承自Dictionary基类,而HashMap则是继承自AbstractMap基类。
Dictionary是任何可将键映射到相应值的类的抽象父类,而AbstractMap则是基于Map接口的骨干实现,以最大限度减少实现此接口所需的工作。 -
2、HashMap可以允许存在一个为null的key和任意个为null的value,但是Hashtable中的key和value都不允许为null,
我们查看源码就知道。
HashMap在进行put操作时,首先会检查key,当遇到key为null时,则会调用putForNullKey()方法来进行处理,而对于value则没有进行任何处理
image.png
而Hashtable在进行put操作时,也会首先检查key,当遇到key为null时,则直接会抛出NullPointerException异常信息。
image.png
- 3、Hashtable是线程安全的,我们查看源码可以看到其中定义的大部分主要方法都加上了synchronized关键字,而HashMap则没有这样做。
如果涉及到多线程同步,一般建议使用Hashtable,然后Collections类中还存在一个静态的方法:synchronizedMap(),该方法创建了一个线程安全的Map对象,并把它作为一个封装好的对象返回,如果是jdk1.5版本以上,可以直接使用CurrentHashMap来处理线程同步问题,它的性能相比于其他两个也会更优。