8.6.2 Java 8改进的HashMap和Hashtable实现类
8.6.2 Java 8改进的HashMap和Hashtable实现类
HashMap和Hashtable都是Map接口的典型实现类,它们之间的关系完全类似于ArrayList和Vector的关系:Hashtable是一个古老的Map实现类,它从JDK1.0起就已经出现了,当**Hashtable出现时,Java还没有提供Map接口**,所以Hashtable包含了两个烦琐的方法:
elements()方法(这个类似于Map接口定义的values()方法)keys()方法(该方法类似于Map接口定义的keySet()方法)
现在很少使用这两个方法。
Java 8改进了HashMap的实现,使用HashMap存在key冲突时依然具有较好的性能。
Hashtable和HashMap的典型区别
除此之外, Hashtable和HashMap存在两点典型区别。
Hashtable线程安全 HashMap线程不安全
Hashtable是一个线程安全的Map实现,但HashMap是线程不安全的实现,所以HashMap比Hashtable的性能高一点;但如果有多个线程访问同一个Map对象时,使用Hashtable实现类会更好。
Hashtable的key和value不能为null HashMap的key和value可以为null
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对,其中1代码处无法将key-value对放入,因为Map中已经有一个key-value对的key为null值,所以无法再放入key为null值的key-value对。2代码处可以放入该key-value对,因为一个HashMap中可以有多个value为null值。编译、运行上面程序,看到如下输出结果:
1 | {null=null, a=null} |
尽量少用Hashtable
从Hashtable的类名上就可以看出它是一个古老的类,它的命名甚至没有遵守Java的命名规范:每个单词的首字母都应该大写。也许当初开发Hashtable的工程师也没有注意到这一点,后来大量Java程序中使用了Hashtable类,所以这个类名也就不能改为HashTable了,否则将导致大量程序需要改写。与Vector类似的是,尽量少用Hashtable实现类。
即使有线程安全要求也不要使用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的可变对象。