
Map
1.4.1 概述
数学中的映射关系在Java中就是通过Map来实现的。它表示,里面存储的元素是一个对(pair),我们通过一个对象,可以在这个映射关系中找到另外一个和这个对象相关的东西。
前面提到的我们对于根据帐号名得到对应的人员的信息,就属于这种情况的应用。我们讲一个人员的帐户名和这人员的信息作了一个映射关系,也就是说,我们把帐户名和人员信息当成了一个“键值对”,“键”就是帐户名,“值”就是人员信息。下面我们先看看Map 接口的常用方法。
1.4.2 常用方法
Map
接口不是 Collection
接口的继承。而是从自己的用于维护键-值关联的接口层次结构入手。按定义,该接口描述了从不重复的键到值的映射。
我们可以把这个接口方法分成三组操作:改变、查询和提供可选视图。
改变操作允许您从映射中添加和除去键-值对。键和值都可以为 null。但是,您不能把 Map 作为一个键或值添加给自身。
Object put(Object key,Object value)
:用来存放一个键
-
值对
Map
中
Object remove(Object key)
:根据
key(
键
)
,移除一个键
-
值对,并将值返回
void putAll(Map mapping)
:将另外一个
Map
中的元素存入当前的
Map
中
void clear()
:清空当前
Map
中的元素
查询操作允许您检查映射内容:
Object get(Object key)
:根据
key(
键
)
取得对应的值
boolean containsKey(Object key)
:判断
Map
中是否存在某键(
key
)
boolean containsValue(Object value):
判断
Map
中是否存在某值
(value)
int size():
返回
Map
中
键-值对的个数
boolean isEmpty()
:判断当前
Map
是否为空
最后一组方法允许您把键或值的组作为集合来处理。
public Set keySet()
:返回所有的键(
key
),并使用
Set
容器存放
public Collection values()
:返回所有的值(
Value
),并使用
Collection
存放
public Set entrySet()
:
返回一个实现 Map.Entry 接口的元素 Set
因为映射中键的集合必须是唯一的,就使用 Set 来支持。因为映射中值的集合可能不唯一,就使用 Collection 来支持。最后一个方法返回一个实现 Map.Entry 接口的元素 Set。
我们看看Map的常用实现类的比较,如下表:
|
简述 |
实现 |
操作特性 |
成员要求 |
Map |
保存键值对成员,基于键找值操作,使用compareTo或compare方法对键进行排序 |
HashMap |
能满足用户对Map的通用需求 |
键成员可为任意Object子类的对象,但如果覆盖了equals方法,同时注意修改hashCode方法。 |
TreeMap |
支持对键有序地遍历,使用时建议先用HashMap增加和删除成员,最后从HashMap生成TreeMap;附加实现了SortedMap接口,支持子Map等要求顺序的操作 |
键成员要求实现Comparable接口,或者使用Comparator构造TreeMap键成员一般为同一类型。 |
||
LinkedHashMap |
保留键的插入顺序,用equals 方法检查键和值的相等性 |
成员可为任意Object子类的对象,但如果覆盖了equals方法,同时注意修改hashCode方法。 |
下面我们看一个简单的例子:
import java.util.*; public class MapTest { public static void main(String[] args) { Map map1 = new HashMap(); Map map2 = new HashMap(); map1.put("1","aaa1"); map1.put("2","bbb2"); map2.put("10","aaaa10"); map2.put("11","bbbb11"); //根据键 "1" 取得值:"aaa1" System.out.println("map1.get(\"1\")="+map1.get("1")); // 根据键 "1" 移除键值对"1"-"aaa1" System.out.println("map1.remove(\"1\")="+map1.remove("1")); System.out.println("map1.get(\"1\")="+map1.get("1")); map1.putAll(map2);//将map2全部元素放入map1中 map2.clear();//清空map2 System.out.println("map1 IsEmpty?="+map1.isEmpty()); System.out.println("map2 IsEmpty?="+map2.isEmpty()); System.out.println("map1 中的键值对的个数size = "+map1.size()); System.out.println("KeySet="+map1.keySet());//set System.out.println("values="+map1.values());//Collection System.out.println("entrySet="+map1.entrySet()); System.out.println("map1 是否包含键:11 = "+map1.containsKey("11")); System.out.println("map1 是否包含值:aaa1 = "+map1.containsValue("aaa1")); } }
运行输出结果为:
map1.get("1")=aaa1
map1.remove("1")=aaa1
map1.get("1")=null
map1 IsEmpty?=false
map2 IsEmpty?=true
map1 中的键值对的个数size = 3
KeySet=[10, 2, 11]
values=[aaaa10, bbb2, bbbb11]
entrySet=[10=aaaa10, 2=bbb2, 11=bbbb11]
map1 是否包含键:11 = true
map1 是否包含值:aaa1 = false
在该例子中,我们创建一个HashMap,并使用了一下Map接口中的各个方法。
其中Map中的entrySet()方法先提一下,该方法返回一个实现 Map.Entry 接口的对象集合。集合中每个对象都是底层 Map 中一个特定的键-值对。
Map.Entry 接口是Map 接口中的一个内部接口,该内部接口的实现类存放的是键值对。在下面的实现原理中,我们会对这方面再作介绍,现在我们先不管这个它的具体实现。
我们再看看排序的Map是如何使用:
import java.util.*; public class MapSortExample { public static void main(String args[]) { Map map1 = new HashMap(); Map map2 = new LinkedHashMap(); for(int i=0;i<10;i++){ double s=Math.random()*100;//产生一个随机数,并将其放入Map中 map1.put(new Integer((int) s),"第 "+i+" 个放入的元素:"+s+"\n"); map2.put(new Integer((int) s),"第 "+i+" 个放入的元素:"+s+"\n"); } System.out.println("未排序前HashMap:"+map1); System.out.println("未排序前LinkedHashMap:"+map2); //使用TreeMap来对另外的Map进行重构和排序 Map sortedMap = new TreeMap(map1); System.out.println("排序后:"+sortedMap); System.out.println("排序后:"+new TreeMap(map2)); } }
该程序的一次运行结果为:
未排序前HashMap:{64=第 1 个放入的元素:64.05341725531845
, 15=第 9 个放入的元素:15.249165766266382
, 2=第 4 个放入的元素:2.66794706854534
, 77=第 0 个放入的元素:77.28814965781416
, 97=第 5 个放入的元素:97.32893518378948
, 99=第 2 个放入的元素:99.99412014935982
, 60=第 8 个放入的元素:60.91451419025399
, 6=第 3 个放入的元素:6.286974058646977
, 1=第 7 个放入的元素:1.8261658496439903
, 48=第 6 个放入的元素:48.736039522423106
}
未排序前LinkedHashMap:{77=第 0 个放入的元素:77.28814965781416
, 64=第 1 个放入的元素:64.05341725531845
, 99=第 2 个放入的元素:99.99412014935982
, 6=第 3 个放入的元素:6.286974058646977
, 2=第 4 个放入的元素:2.66794706854534
, 97=第 5 个放入的元素:97.32893518378948
, 48=第 6 个放入的元素:48.736039522423106
, 1=第 7 个放入的元素:1.8261658496439903
, 60=第 8 个放入的元素:60.91451419025399
, 15=第 9 个放入的元素:15.249165766266382
}
排序后:{1=第 7 个放入的元素:1.8261658496439903
, 2=第 4 个放入的元素:2.66794706854534
, 6=第 3 个放入的元素:6.286974058646977
, 15=第 9 个放入的元素:15.249165766266382
, 48=第 6 个放入的元素:48.736039522423106
, 60=第 8 个放入的元素:60.91451419025399
, 64=第 1 个放入的元素:64.05341725531845
, 77=第 0 个放入的元素:77.28814965781416
, 97=第 5 个放入的元素:97.32893518378948
, 99=第 2 个放入的元素:99.99412014935982
}
排序后:{1=第 7 个放入的元素:1.8261658496439903
, 2=第 4 个放入的元素:2.66794706854534
, 6=第 3 个放入的元素:6.286974058646977
, 15=第 9 个放入的元素:15.249165766266382
, 48=第 6 个放入的元素:48.736039522423106
, 60=第 8 个放入的元素:60.91451419025399
, 64=第 1 个放入的元素:64.05341725531845
, 77=第 0 个放入的元素:77.28814965781416
, 97=第 5 个放入的元素:97.32893518378948
, 99=第 2 个放入的元素:99.99412014935982
}
从运行结果,我们可以看出,HashMap的存入顺序和输出顺序无关。而LinkedHashMap 则保留了键值对的存入顺序。TreeMap则是对Map中的元素进行排序。在实际的使用中我们也经常这样做:使用HashMap或者LinkedHashMap 来存放元素,当所有的元素都存放完成后,如果使用则是需要一个经过排序的Map的话,我们再使用TreeMap来重构原来的Map对象。这样做的好处是:因为HashMap和LinkedHashMap 存储数据的速度比直接使用TreeMap 要快,存取效率要高。当完成了所有的元素的存放后,我们再对整个的Map中的元素进行排序。这样可以提高整个程序的运行的效率,缩短执行时间。
这里需要注意的是,TreeMap中是根据键(Key)进行排序的。而如果我们要使用TreeMap来进行正常的排序的话,Key 中存放的对象必须实现Comparable 接口。