8.6.2 Java 8改进的HashMap和Hashtable实现类
HashMap
和Hashtable
都是Map
接口的典型实现类,它们之间的关系完全类似于ArrayList
和Vector
的关系: Hashtable
是一个古老的Map
实现类,它从JDK1.0
起就已经出现了,当它出现时,Java
还没有提供Map
接口,所以它包含了两个烦琐的方法,即elements()
方法(这个类似于Map
接口定义的values()
方法)和keys()
方法(该方法类似于Map
接口定义的keySet()
方法),现在很少使用这两个方法.Java 8
改进了HashMap
的实现,使用HashMap
存在key
冲突时依然具有较好的性能.
Hashtable和HashMap的典型区别
除此之外, Hashtable
和HashMap
存在两点典型区别。
Hashtable
是一个线程安全的Map
实现,但HashMap
是线程不安全的实现,所以HashMap
比Hashtable
的性能高一点;但如果有多个线程访问同一个Map
对象时,使用Hashtable
实现类会更好。Hashtable
不允许使用null
作为key
和value
,如果试图把null
值放进Hashtable
中,将会引发NullPointerException
异常;但HashMap
可以使用null
作为key
或value
由于HashMap
里的key
不能重复,value
可以重复,所以HashMap
里最多只有一个key-value
对的key
为null
但可以有无数多个key-value
对的value
为null
下面程序示范了用null
值作为HashMap
的key
和value
的情形。
实例 null作为HashMap的key和value的情况
1 | import java.util.*; |
上面程序试图向HashMap
中放入三个key-value
对,其中①代码处无法将key-value
对放入,因为Map
中已经有一个key-value
对的key
为null
值,所以无法再放入key
为null
值的key-value
对。②代码处可以放入该key-value
对,因为一个HashMap
中可以有多个value
为null
值。编译、运行上面程序,看到如下输出结果:
1 | {null=null, a=null} |
尽量少用Hashtable
从Hashtable
的类名上就可以看出它是一个古老的类,它的命名甚至没有遵守Java
的命名规范:每个单词的首字母都应该大写。也许当初开发Hashtable
的工程师也没有注意到这一点,后来大量Java
程序中使用了Hashtable
类,所以这个类名也就不能改为HashTable
了,否则将导致大量程序需要改写。与Vector
类似的是,尽量少用Hashtable
实现类,即使需要创建线程安全的Map
实现类,也无须使用Hashtable
实现类,可以通过后面介绍的Collections
工具类把HashMap
变成线程安全的。
HashMap中作为key对象要满足什么条件
为了成功地在HashMap
、 Hashtable
中存储、获取对象,用作key
的对象必须实现hashCode()
方法和equals()
方法。
HashMap判断两个key相等的标准是什么
与HashSet
集合不能保证元素的顺序一样, HashMap
、 Hashtable
也不能保证其中key-value
对的顺序。类似于HashSet
, HashMap
、 Hashtable
判断两个key
相等的标准也是:两个key
通过equals
方法比较返回true
,并且两个key
的hashCode
值也相等
HashMap判断两个value相等的标准是什么
除此之外, HashMap
、 Hashtable
中还包含一个containsValue()
方法,用于判断是否包含指定的value
那么HashMap
、 Hashtable
如何判断两个value
相等呢? HashMap
、 Hashtable
判断两个value
相等的标准更简单:只要两个对象通过equals
方法比较返回true
即可。下面程序示范了Hashtable
判断两个key
相等的标准和两个value
相等的标准。
1 | import java.util.*; |
上面程序定义了A类和B类,其中
- A类判断两个A对象相等的标准是
count
实例变量:只要两个A对象的count
变量相等,则通过equals
方法比较它们返回true
,它们的hashCode
值也相等; - 而B对象则可以与任何对象相等。
Hashtable
判断value
相等的标准是: value
与另外一个对象通过equals()
方法比较返回true
即可。上面程序中的ht
对象中包含了一个B对象,它与任何对象通过equals()
方法比较总是返回true
,所以在①代码处返回true
在这种情况下,不管传给ht
对象的containtsValue()
方法参数是什么,程序总是返回true
.
根据Hashtable
判断两个key
相等的标准,程序在②处也将输出true
,虽然两个A对象虽然不是同个对象,但它们通过equals
方法比较返回tue
,且hashCode
值相等, Hashtable
就认为它们是同一个key
。类似的是,程序在③处也可以删除对应的key-value
对。
equals方法和hashCode方法判断标准要一致
当使用自定义类作为HashMap
、 Hashtable
的key
时,如果重写该类的equals()
方法和hashCode()
方法,则应该保证两个方法的判断标准一致,也就是当两个key
通过equals
方法比较返回true
时,两个key
的hashCode()
返回值也应该相同。因为HashMap
、 Hashtable
保存key
的方式与HashSet
保存集合元素的方式完全相同,所以HashMap
、 Hashtable
对key
的要求与HashSet
对集合元素的要求完全相同。
可变对象作为HashMap的key时可能无法正确访问
与HashSet
类似的是,如果使用可变对象作为HashMap
、 Hashtable
的key
,并且程序修改了作为key
的可变对象,则也可能出现与HashSet
类似的情形:程序再也无法准确访问到Map
中被修改过的key
。看下面程序
1 | import java.util.*; |
该程序使用了前一个程序定义的A类实例作为key
,而A对象是可变对象。当程序在①处修改了A对象后,实际上修改了HashMap
集合中元素的key
,这就导致该key
不能被准确访问。当程序试图删除count
为87563
的A对象时,只能删除没被修改的key
所对应的key-value
对。程序②和③处的代码都不能访问”疯狂Java讲义
“字符串,这都是因为它对应的key
被修改过的原因。
尽量不要使用可变对象作为HashMap的key
与HashSet
类似的是,尽量不要使用可变对象作为HashMap
、 Hashtable
的key
,如果确实需要使用可变对象作为HashMap
、 Hashtable
的key
,则尽量不要在程序中修改作为key
的可变对象。