首页 > 代码库 > 容器深入研究 --- 理解Map

容器深入研究 --- 理解Map


通常的:

映射表(也称关联数组)的基本思想是它维护的键-值(对)关联,因此你可以使用键来查找值。

标准的Java类库中包含了Map的几种实现,包括:HashMap,TreeMap,LinkedHashMap,WeakHashMap,ConcurrentHashMap,IdentityHashMap。

它们都有同样的基本接口Map,但是行为特性各不相同,这表现在效率、键值对的保存及呈现次序、对象的保存周期、映射表如何在多线程程序中工作的判定“键”等价的策略方面。Map接口实现的数量应该让你感觉到这种工具的重要性。

你可以获的对Map更深入的理解,这有助于观察关联数组是如何创建的。下面是一个极其简单的实现。

class AssociativeArray<K,V> {
	private Object[][] pairs;
	private int index;
	
	public AssociativeArray(int length) {
		pairs = new Object[length][2];
	}
	
	public void put(K key, V value) {
		if (index >= pairs.length) {
			throw new ArrayIndexOutOfBoundsException();
		}
		pairs[index++] = new Object[]{key, value};
	}
	
	public V get(K key) {
		for (int i = 0; i < index; i ++) {
			if (key.equals(pairs[i][0])) {
				return (V) pairs[i][1];
			}
		}
		return null;
	}
	
	@Override
	public String toString() {
		StringBuffer result = new StringBuffer();
		for (int i = 0; i < index; i++) {
			result.append(pairs[i][0].toString());
			result.append(":");
			result.append(pairs[i][1].toString());
			if (i < index - 1) {
				result.append("\n");
			}
		}
		return result.toString();
	}
}

上面的本版是简单性的、缺乏效率性的,并且由于具有固定的尺寸显得够不灵活。

性能:

性能是映射表中的一个重要问题,当在get()中使用线性搜索时,执行速度回相当的慢,而这正是HashMap提高速度的地方。

HashMap使用了特殊的值,称作散列码,来取代对键的缓慢搜索。散列码是“相对唯一”的、用以代表对象的int值,它是通过将该对象的某些信息进行转换搜索而成的。

hashCode()是根类Object中的方法,因此所有Java对象都能产生散列码,HashMap就是使用对象的hashCode()进行快速查询的,此方法可以显著的提高性能

下面是基本的Map实现。在HashMap上打星号表示如果没有其他的限制,它就应该成为你的默认选择,因为它对速度进行了优化。其他的都强调其他的特性,因此都不如HashMap快。

HashMap:

Map基于散列表的实现(它取代了HashTable)。插入和查询“键值对”的开销是固定的。可以通过构造器设置容量和负载因子,以调整容器的性能

LinkedHashMap:

类似于HashMap,但是迭代遍历时,取得“键值对”的顺序是其插入次序,或者是最近最少使用(LRU)的次序。只比HashMap慢一点;而在迭代访问时反映更快,因为它使用的链表维护内部次序

TreeMap:

基于红黑树的实现。查看“键”或“键值对”时,它们会被排序(次序由Comparable或者Comparator决定)。TreeMap特点在于,所得到的结果是经过排序的。TreeMap是唯一的带有subMap()方法的Map,它可以返回一个子树。

WeakHashMap:

弱键(weak key)映射,允许释放映射所指向的对象;这是为解决某类特殊问题而设计的。如果映射之外没有引用指向某个“键”,则此“键”可以被垃圾回收器回收。

ConcurrentHashMap:

一种线程安全的Map,它不涉及同步加锁。

IdentityHashMap:

使用==代替equals()对“键”进行比较的散列映射。专为解决特殊问题设计的。

 散列是映射中存储元素的最常用的方式。

对Map中使用的键的要求与对Set中的元素的要求一样。任何键都必须有一个equals()方法;如果键被用于散列Map,那么它必须还具有恰当的hashCode()方法;如果键被用于TreeMap它必须实现Comparable。


SortedMap:

用SortedMap(TreeMap时期现阶段的唯一实现),可以确保处于排序状态。这使得它具有额外的功能,这些功能是由SortedMap接口中的下列方法提供给的:

 Comparator<? superK>comparator()
          返回对此映射中的键进行排序的比较器;如果此映射使用键的自然顺序,则返回null
 Set<Map.Entry<K,V>>entrySet()
          返回在此映射中包含的映射关系的 Set 视图。
 KfirstKey()
          返回此映射中当前第一个(最低)键。
 SortedMap<K,V>headMap(K toKey)
          返回此映射的部分视图,其键值严格小于 toKey
 Set<K>keySet()
          返回在此映射中所包含键的 Set 视图。
 KlastKey()
          返回映射中当前最后一个(最高)键。
 SortedMap<K,V>subMap(K fromKey,K toKey)
          返回此映射的部分视图,其键值的范围从 fromKey(包括)到 toKey(不包括)。
 SortedMap<K,V>tailMap(K fromKey)
          返回此映射的部分视图,其键大于等于 fromKey
 Collection<V>values()
          返回在此映射中所包含值的 Collection 视图。

LindedHashMap:

为了提高速度,LinkedHashMap散列化所有的元素,但是在遍历键值对时,却又以元素的插入顺序返回键值对(System.out.println())。

此外,可以在构造器中设定LinkedHashMap,使之采用基于访问最近最少使用(LRU)算法,于是没有访问过的(可被看做需要删除的)元素就会出现在队列的掐面,对于需要定期清理元素以节省空间的程序来说,此功能是很容易实现的。