1、 map HashMap() ; Iterator it map.entrySet().iterator() while (it.hasNext() Map.Entry entry (Map.Entry) it.next() Object key entry.getKey() value entry.getValue() 了解最常用的集合类型之一 Map 的基础知识以及如何针对您应用程序特有的数据优化 Map。本文相关下载: Jack 的 HashMap 测试 Oracle JDeveloper 10g java.util 中的集合类包含 Java 中某些最常用的类。 最常用的集合类是 Lis
2、t 和 Map。 List 的具体实现包括 ArrayList 和 Vector,它们是可变大小的列表,比较适合构建、存储和操作任何类型对象元素列表。 List 适用于按数值索引访问元素的情形。Map 提供了一个更通用的元素存储方法。 Map 集合类用于存储元素对(称作“键”和“值”),其中每个键映射到一个值。 从概念上而言,您可以将 List 看作是具有数值键的 Map。 而实际上,除了 List 和 Map 都在定义 java.util 中外,两者并没有直接的联系。本文将着重介绍核心 Java 发行套件中附带的 Map,同时还将介绍如何采用或实现更适用于您应用程序特定数据的专用 Map。了
3、解 Map 接口和方法 Java 核心类中有很多预定义的 Map 类。 在介绍具体实现之前,我们先介绍一下 Map 接口本身,以便了解所有实现的共同点。 Map 接口定义了四种类型的方法,每个 Map 都包含这些方法。 下面,我们从两个普通的方法(表 1)开始对这些方法加以介绍。表 1: 覆盖的方法。 我们将这 Object 的这两个方法覆盖,以正确比较 Map 对象的等价性。equals(Object o)比较指定对象与此 Map 的等价性hashCode()返回此 Map 的哈希码Map 构建 Map 定义了几个用于插入和删除元素的变换方法(表 2)。表 2: Map 更新方法: 可以更改
4、 Map 内容。clear()从 Map 中删除所有映射remove(Object key)从 Map 中删除键和关联的值put(Object key, Object value)将指定值与指定键相关联putAll(Map t)将指定 Map 中的所有映射复制到此 map尽管您可能注意到,纵然假设忽略构建一个需要传递给 putAll() 的 Map 的开销,使用 putAll() 通常也并不比使用大量的 put() 调用更有效率,但 putAll() 的存在一点也不稀奇。 这是因为,putAll() 除了迭代 put() 所执行的将每个键值对添加到 Map 的算法以外,还需要迭代所传递的 Ma
5、p 的元素。 但应注意,putAll() 在添加所有元素之前可以正确调整 Map 的大小,因此如果您未亲自调整 Map 的大小(我们将对此进行简单介绍),则 putAll() 可能比预期的更有效。查看 Map 迭代 Map 中的元素不存在直接了当的方法。 如果要查询某个 Map 以了解其哪些元素满足特定查询,或如果要迭代其所有元素(无论原因如何),则您首先需要获取该 Map 的“视图”。 有三种可能的视图(参见表 3) 所有键值对 参见 entrySet() 所有键 参见 keySet() 有值 参见 values() 前两个视图均返回 Set 对象,第三个视图返回 Collection 对象
6、。 就这两种情况而言,问题到这里并没有结束,这是因为您无法直接迭代 Collection 对象或 Set 对象。要进行迭代,您必须获得一个 Iterator 对象。 因此,要迭代 Map 的元素,必须进行比较烦琐的编码 Iterator keyValuePairs = aMap.entrySet().iterator();Iterator keys = aMap.keySet().iterator();Iterator values = aMap.values().iterator();值得注意的是,这些对象(Set、Collection 和 Iterator)实际上是基础 Map 的视图,而
7、不是包含所有元素的副本。 这使它们的使用效率很高。 另一方面,Collection 或 Set 对象的 toArray() 方法却创建包含 Map 所有元素的数组对象,因此除了确实需要使用数组中元素的情形外,其效率并不高。我运行了一个小测试(随附文件中的),该测试使用了 HashMap,并使用以下两种方法对迭代 Map 元素的开销进行了比较:int mapsize = aMap.size();Iterator keyValuePairs1 = aMap.entrySet().iterator();for (int i = 0; i (Map 大小),则调整 Map 大小 例如,如果默认负载因子
8、为 0.75,默认容量为 11,则 11 x 0.75 = 8.25,该值向下取整为 8 个元素。 因此,如果将第 8 个项添加到此 Map,则该 Map 将自身的大小调整为一个更大的值。 相反,要计算避免调整大小所需的初始容量,用将要添加的项数除以负载因子,并向上取整,例如, 对于负载因子为 0.75 的 100 个项,应将容量设置为 100/0.75 = 133.33,并将结果向上取整为 134(或取整为 135 以使用奇数) 奇数个存储桶使 map 能够通过减少冲突数来提高执行效率。 虽然我所做的测试(关联文件中的 并未表明质数可以始终获得更好的效率,但理想情形是容量取质数。 1.4 版
9、后的某些 Map(如 HashMap 和 LinkedHashMap,而非 Hashtable 或 IdentityHashMap)使用需要 2 的幂容量的哈希函数,但下一个最高 2 的幂容量由这些 Map 计算,因此您不必亲自计算。负载因子本身是空间和时间之间的调整折衷。 较小的负载因子将占用更多的空间,但将降低冲突的可能性,从而将加快访问和更新的速度。 使用大于 0.75 的负载因子可能是不明智的,而使用大于 1.0 的负载因子肯定是不明知的,这是因为这必定会引发一次冲突。 使用小于 0.50 的负载因子好处并不大,但只要您有效地调整 Map 的大小,应不会对小负载因子造成性能开销,而只会
10、造成内存开销。 但较小的负载因子将意味着如果您未预先调整 Map 的大小,则导致更频繁的调整大小,从而降低性能,因此在调整负载因子时一定要注意这个问题。选择适当的 Map 应使用哪种 Map? 它是否需要同步? 要获得应用程序的最佳性能,这可能是所面临的两个最重要的问题。 当使用通用 Map 时,调整 Map 大小和选择负载因子涵盖了 Map 调整选项。以下是一个用于获得最佳 Map 性能的简单方法 1. 将您的所有 Map 变量声明为 Map,而不是任何具体实现,即不要声明为 HashMap 或 Hashtable,或任何其他 Map 类实现。Map criticalMap = new Ha
11、shMap(); /好 HashMap criticalMap = new HashMap(); /差 这使您能够只更改一行代码即可非常轻松地替换任何特定的 Map 实例。2. 下载 Doug Lea 的 util.concurrent 程序包 (http:/gee.cs.oswego.edu/dl/classes/EDU/oswego/cs/dl/util/concurrent/intro.html)。 将 ConcurrentHashMap 用作默认 Map。 当移植到 1.5 版时,将 java.util.concurrent.ConcurrentHashMap 用作您的默认 Map。
12、不要将 ConcurrentHashMap 包装在同步的包装器中,即使它将用于多个线程。 使用默认大小和负载因子。3. 监测您的应用程序。 如果发现某个 Map 造成瓶颈,则分析造成瓶颈的原因,并部分或全部更改该 Map 的以下内容: Map 类;Map 大小;负载因子;关键对象 equals() 方法实现。 专用的 Map 的基本上都需要特殊用途的定制 Map 实现,否则通用 Map 将实现您所需的性能目标。Map 选择 也许您曾期望更复杂的考量,而这实际上是否显得太容易? 好的,让我们慢慢来。 首先,您应使用哪种 Map? 答案很简单: 不要为您的设计选择任何特定的 Map,除非实际的设计需要指定一个特殊类型的 Map。 设计时通常不需要选择具体的 Map 实现。 您可能知道自己需要一个 Map,但不知道使用哪种。 而这恰恰就是使用 Map 接口的意义所在。 直到需要时再选择 Map 实现 如果随处使用“Map”声明的变量,则更改应用程序中任何特殊 Map 的 Map 实现只需要更改一行,这是一种开销很少的调整选择。 是否要使用默认的 Map 实现? 我很快将谈到这个问题。同步 Map 同步与否有何差别? (对于同步,您既可以使用同步的 Map,也可以使用 Collections.sy
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1