18.2.3 Map映射接口

Map没有继承Collection接口,其提供key到value的映射。Map中不能包含相同的key,每个key只能映射一个value。Map接口提供3种集合的视图,Map的内容可以被当作一组key集合、一组value集合或一组key-value映射。

1.Hashtable哈希表类

Hashtable继承Map接口,实现一个key-value映射的哈希表。任何非空(non-null)的对象都可作为key或value。添加数据使用put(key, value)方法,取出数据使用get(key)方法,这两个基本操作的时间开销为常数。

Hashtable通过initial capacity和load factor两个参数调整性能。通常默认的load factor 0.75较好地实现了时间和空间的均衡,增大loadfactor可以节省空间,但相应的查找时间将增大,这会影响像get和put这样的操作。

【实例18.2】演示Hashtable的简单示例,将1、2、3放到Hashtable中,它们的key分别是“one”、“two”、“three”,详细代码如下所示。


01 Hashtable numbers=new Hashtable();

02 numbers.put("one",new Integer(1));

03 numbers.put("two",new Integer(2));

04 numbers.put("three"",new integer(3));

05要取出一个数,比如2,用相应的key:

06 Integer n=(Integer)numbers.get("two");

07 System.out.println("two="+n);


【代码说明】这里只演示Hashtable的用法,并不是完整的实例,读者可将其添加到main主函数中,自己来测试一下运行效果。

作为key的对象,将通过计算其散列函数确定与之对应的value位置,因此任何key的对象都必须实现hashCode()和equals()方法。hashCode()和equals()方法继承于根类Object。如果用自定义的类当作key,则要相当小心。按照散列函数的定义,如果两个对象相同,即obj1.equals(obj2)=true,则它们的hashCode必须相同。但如果两个对象不同,则它们的hashCode不一定不同。如果两个不同对象的hashCode相同,这种现象称为冲突。冲突会导致操作哈希表的时间开销增大,所以尽量定义好的hashCode()方法,这能加快哈希表的操作。

说明

如果相同的对象有不同的hashCode,对哈希表的操作会出现意想不到的结果(期待的get()方法返回null)。要避免这种问题,只需要牢记一条:要同时复写equals()方法和hashCode()方法,而不要只写其中一个。Hashtable是同步的。

2.HashMap哈希映射类

HashMap和Hashtable类似,不同之处在于HashMap是非同步的,并且允许null,即null value和null key。如将HashMap视为Collection时(values()方法可返回Collection),其迭代子操作时间开销和HashMap的容量成比例。因此,如果迭代操作的性能相当重要,切记不要将HashMap的初始化容量设得过高,或者load factor过低。

3.WeakHashMap弱哈希映射类

WeakHashMap是一种改进的HashMap,它对key实行“弱引用”,如果一个key不再被外部所引用,那么该key可以被GC回收。

总结:

❑如果涉及堆栈、队列等操作。应该考虑用List。对于需要快速插入、删除元素的数据集合,建议使用LinkedList。如果需要快速随机访问元素,应该使用ArrayList。

❑如果程序在单线程环境中,或者访问仅仅在一个线程中进行,考虑非同步的类,其效率较高。如果多个线程可能同时操作一个类,应该使用同步的类。

❑要特别注意对哈希表的操作,作为key的对象要正确复写equals()和hashCode()方法。尽量返回接口而非实际的类型,如返回List而非ArrayList,这样如果以后需要将ArrayList换成LinkedList,客户端代码不用改变,这就是针对抽象的编程。