Map以按键/数值对的形式存储数据,和数组非常相似,在数组中存在的索引,它们本身也是对象。
Map的接口
Map---实现Map
Map.Entry--Map的内部类,描述Map中的按键/数值对。
SortedMap---扩展Map,使按键保持升序排列
关于怎么使用,一般是选择Map的子类,而不直接用Map类。
下面以HashMap为例。
public static void main(String args[])
{
HashMap hashmap = new HashMap();
hashmap.put("Item0", "Value0");
hashmap.put("Item1", "Value1");
hashmap.put("Item2", "Value2");
hashmap.put("Item3", "Value3");
Set set = hashmap.entrySet();
Iterator iterator = set.iterator();
while (iterator.hasNext()
{
Map.Entry mapentry = (Map.Entry) iterator.next();
System.out.println(mapentry.getkey() + "/" + mapentry.getValue());
}
}
注意,这里Map的按键必须是唯一的,比如说不能有两个按键都为null。
如果用过它,就会知道它的用处了。
资料:
java.util 中的集合类包含 Java 中某些最常用的类。 最常用的集合类是 List 和 Map。 List 的具体实现包括 ArrayList 和 Vector,它们是可变大小的列表,比较适合构建、存储和操作任何类型对象的元素列表。 List 适用于按数值索引访问元素的情形。
Map 提供了一个更通用的元素存储方法。 Map 集合类用于存储元素对(称作“键”和“值”),其中每个键映射到一个值。 从概念上而言,您可以将 List 看作是具有数值键的 Map。 而实际上,除了 List 和 Map 都在定义 java.util 中外,两者并没有直接的联系。本文将着重介绍核心 Java 发行套件中附带的 Map,同时还将介绍如何采用或实现更适用于您应用程序特定数据的专用 Map。
了解 Map 接口和方法
Java 核心类中有很多预定义的 Map 类。 在介绍具体实现之前,我们先介绍一下 Map 接口本身,以便了解所有实现的共同点。 Map 接口定义了四种类型的方法,每个 Map 都包含这些方法。 下面,我们从两个普通的方法(表 1)开始对这些方法加以介绍。
表 1: 覆盖的方法。 我们将这 Object 的这两个方法覆盖,以正确比较 Map 对象的等价性。
equals(Object o)
比较指定对象与此 Map 的等价性
hashCode()
返回此 Map 的哈希码
Map 构建
Map 定义了几个用于插入和删除元素的变换方法(表 2)。
表 2: Map 更新方法: 可以更改 Map 内容。
clear()
从 Map 中删除所有映射
remove(Object key)
从 Map 中删除键和关联的值
put(Object key, Object value)
将指定值与指定键相关联
clear()
从 Map 中删除所有映射
putAll(Map t)
将指定 Map 中的所有映射复制到此 map
尽管您可能注意到,纵然假设忽略构建一个需要传递给 putAll() 的 Map 的开销,使用 putAll() 通常也并不比使用大量的 put() 调用更有效率,但 putAll() 的存在一点也不稀奇。 这是因为,putAll() 除了迭代 put() 所执行的将每个键值对添加到 Map 的
查看 Map
迭代 Map 中的元素不存在直接了当的方法。 如果要查询某个 Map 以了解其哪些元素满足特定查询,或如果要迭代其所有元素(无论原因如何),则您首先需要获取该 Map 的“视图”。 有三种可能的视图(参见表 3)
所有键值对 — 参见 entrySet()
所有键 — 参见 keySet()
所有值 — 参见 values()
前两个视图均返回 Set 对象,第三个视图返回 Collection 对象。 就这两种情况而言,问题到这里并没有结束,这是因为您无法直接迭代 Collection 对象或 Set 对象。要进行迭代,您必须获得一个 Iterator 对象。 因此,要迭代 Map 的元素,必须进行比较烦琐的编码
Iterator keyValuePairs = aMap.entrySet().iterator();Iterator keys = aMap.keySet().iterator();Iterator values = aMap.values().iterator();
值得注意的是,这些对象(Set、Collection 和 Iterator)实际上是基础 Map 的视图,而不是包含所有元素的副本。 这使它们的使用效率很高。 另一方面,Collection 或 Set 对象的 toArray() 方法却创建包含 Map 所有元素的数组对象,因此除了确实需要使用数组中元素的情形外,其效率并不高。
我运行了一个小测试(随附文件中的 Test1),该测试使用了 HashMap,并使用以下两种方法对迭代 Map 元素的开销进行了比较:
int mapsize = aMap.size();Iterator keyValuePairs1 = aMap.entrySet().iterator();for (int i = 0; i < mapsize; i++){ Map.Entry entry = (Map.Entry) keyValuePairs1.next(); Object key = entry.getKey(); Object value = entry.getValue(); ...}Object[] keyValuePairs2 = aMap.entrySet().toArray();for (int i = 0; i < rem; i++) {{ Map.Entry entry = (Map.Entry) keyValuePairs2[i]; Object key = entry.getKey();
Profilers in Oracle JDeveloper
Oracle JDeveloper 包含一个嵌入的监测器,它测量内存和执行时间,使您能够快速识别代码中的瓶颈。 我曾使用 Jdeveloper 的执行监测器监测 HashMap 的 containsKey() 和 containsValue() 方法,并很快发现 containsKey() 方法的速度比 containsValue() 方法慢很多(实际上要慢几个数量级!)。 (参见图 1 和图 2,以及随附文件中的 Test2 类)。
Object value = entry.getValue(); ...}
此测试使用了两种测量方法: 一种是测量迭代元素的时间,另一种测量使用 toArray 调用创建数组的其他开销。 第一种方法(忽略创建数组所需的时间)表明,使用已从 toArray 调用中创建的数组迭代元素的速度要比使用 Iterator 的速度大约快 30%-60%。 但如果将使用 toArray 方法创建数组的开销包含在内,则使用 Iterator 实际上要快 10%-20%。 因此,如果由于某种原因要创建一个集合元素的数组而非迭代这些元素,则应使用该数组迭代元素。 但如果您不需要此中间数组,则不要创建它,而是使用 Iterator 迭代元素。
表 3: 返回视图的 Map 方法: 使用这些方法返回的对象,您可以遍历 Map 的元素,还可以删除 Map 中的元素。
entrySet()
返回 Map 中所包含映射的 Set 视图。 Set 中的每个元素都是一个 Map.Entry 对象,可以使用 getKey() 和 getValue() 方法(还有一个 setValue() 方法)访问后者的键元素和值元素
keySet()
返回 Map 中所包含键的 Set 视图。 删除 Set 中的元素还将删除 Map 中相应的映射(键和值)
values()
返回 map 中所包含值的 Collection 视图。 删除 Collection 中的元素还将删除 Map 中相应的映射(键和值)
访问元素
表 4 中列出了 Map 访问方法。Map 通常适合按键(而非按值)进行访问。 Map 定义中没有规定这肯定是真的,但通常您可以期望这是真的。 例如,您可以期望 containsKey() 方法与 get() 方法一样快。 另一方面,containsValue() 方法很可能需要扫描 Map 中的值,因此它的速度可能比较慢。
表 4: Map 访问和测试方法: 这些方法检索有关 Map 内容的信息但不更改 Map 内容。
get(Object key)
返回与指定键关联的值
containsKey(Object key)
如果 Map 包含指定键的映射,则返回 true
containsValue(Object value)
如果此 Map 将一个或多个键映射到指定值,则返回 true
isEmpty()
如果 Map 不包含键-值映射,则返回 true
size()
返回 Map 中的键-值映射的数目
对使用 containsKey() 和 containsValue() 遍历 HashMap 中所有元素所需时间的测试表明,containsValue() 所需的时间要长很多。 实际上要长几个数量级! (参见图 1 和图 2,以及随附文件中的 Test2)。 因此,如果 containsValue() 是应用程序中的性能问题,它将很快显现出来,并可以通过监测您的应用程序轻松地将其识别。 这种情况下,我相信您能够想出一个有效的替换方法来实现 containsValue() 提供的等效功能。 但如果想不出办法,则一个可行的解决方案是再创建一个 Map,并将第一个 Map 的所有值作为键。 这样,第一个 Map 上的 containsValue() 将成为第二个 Map 上更有效的 containsKey()。