一、容器键值对:
1.HashMap 的 key 和 value 都允许为 null , HashMap 在 key 为 null 的时候,值必须为null。
2.Hashtable 的 key 和 value 都不允许为 null 。 Hashtable 遇到key或value为 null时 ,将抛出 NullPointerException异常 。
二、容量设定与扩容机制:
1.HashMap 默认初始化容量为 16,并且容器容量一定是 2 的 n 次方。当元素数量达到容量和加载因子(加载因子LoadFactor,默认为0.75)的乘积时会触发扩容,并且是以原容量 2 倍 的方式 进行扩容。
2.Hashtable 默认初始化容量为 11。
在元素数量达到容量和加载因子(加载因子默认是0.75)的乘积时会进行扩容,是以原容量 2 倍 再加 1 的方式进行扩容。即 int newCapacity = (oldCapacity << 1) + 1; 。
三、存储位置:
1.HashMap 是先将 key 键的 hashCode 经过扰动函数扰动后得到 hash 值,然后再利用 hash & (length - 1) 的方式(由于 HashMap 的容器容量一定是 2 的 n 次方,所以可以使用 hash & (length- 1) 的方式代替取模的方式计算元素的位置从而提高运算效率)代替取模,得到元素的存储位置。JDK 1.8之后HashMap引入了红黑树来优化链表过长的问题。
2.Hashtable 是使用除留余数法进行计算存储位置的(因为其默认容量不是2 的 n 次方,故无法用位运算替代模运算), int index = (hash &0x7FFFFFFF) % tab.length; 。
四、线程安全:
1.HashMap 不是线程安全,如果想线程安全,可以通过调用Collections.synchronizedMap(Map<K,V> m) 使其线程安全。或使用 ConcurrentHashMap 容器以同样达到线程安全。
2.Hashtable 是线程安全的,每个操作方法都有 synchronized 修饰使其同步,但运行效率不高,所以建议使用 ConcurrentHashMap 容器以达到线程安全。
3.总结:
1)Hashtable 是一个古老的容器,如果我们不需要线程同步,则可以使用HashMap ,如果需要线程同步,则可以使用 ConcurrentHashMap 。
2)HashMap不是同步的,所以性能会比Hashtable要高。
五、遍历及访问
1.HashMap和Hashtable都支持使用Iterator进行遍历。但是,Hashtable还额外支持使用Enumeration进行遍历,但此方式已过时。
2.HashMap 的迭代器(Iterator)是 fail-fast 的。如在迭代过程中需要修改 HashMap的结构,除了通过迭代器的 remove() 方法是安全的之外,调用其他方法都会抛出 ConcurrentModificationException 异常。
Hashtable 的迭代器不是 fail-fast 的。
3.HashMap迭代顺序不确定。Hashtable迭代顺序按照插入顺序进行。
4.Hashtable保留了containsKey、containsValue、contains三个方法,用于检查是否包含某个键、值或键值对。
HashMap去掉了contains方法,只保留了containsKey和containsValue两个方法。
六、其他
1.HashMap继承自AbstractMap,实现了Map、Cloneable、Serializable接口。
2.Hashtable继承自Dictionary,实现了Map、Cloneable、Serializable接口。
微风不燥,阳光正好,你就像风一样经过这里,愿你停留的片刻温暖舒心。
我是程序员小迷(致力于C、C++、Java、Kotlin、Android、Shell、JavaScript、TypeScript、Python等编程技术的技巧经验分享),若作品对您有帮助,请关注、分享、点赞、收藏、在看、喜欢,您的支持是我们为您提供帮助的最大动力。
欢迎关注。助您在编程路上越走越好!