式下几乎要多用两倍的时间! 表 5: 填充已预先设置大小的 HashMap 与填充默认大小的 HashMap 所需时间的比较 客户端模式服务器模式预先设置的大小100% 100%默认大小294%157% 使用负载因子 为确定何时调整大小,而不是对每个存储桶中的链接列表的深度进行记数,基于哈希的 Map 使用一个额外参数并粗略计算存储桶的密度。 Map 在调整大小之前,使用名为“负载因子”的参数指示 Map 将承担的“负载”量,即它的负载程度。 负载因子、项数(Map 大小)与容量之间的关系简单明了: •
如果(负载因子)x(容量)>(Map 大小),则调整 Map 大小 例如,如果默认负载因子为 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 版后的某些 Map(如 HashMap 和 LinkedHashMap,而非 Hashtable 或 IdentityHashMap)使用需要 2 的幂容量的哈希函数,但下一个最高 2 的幂容量由这些 Map 计算,因此您不必亲自计算。 负载因子本身是空间和时间之间的调整折衷。 较小的负载因子将占用更多的空间,但将降低冲突的可能性,从而将加快访问和更新的速度。 使用大于 0.75 的负载因子可能是不明智的,而使用大于 1.0 的负载因子肯定是不明知的,这是因为这必定会引发一次冲突。 使用小于 0.50 的负载因子好处并不大,但只要您有效地调整 Map 的大小,应不会对小负载因子造成性能开销,而只会造成内存开销。 但较小的负载因子将意味着如果您未预先调整 Map 的大小,则导致更频繁的调整大小,从而降低性能,因此在调整负载因子时一定要注意这个问题。 选择适当的 Map 应使用哪种 Map? 它是否需要同步? 要获得应用
程序的最佳性能,这可能是所面临的两个最重要的问题。 当使用通用 Map 时,调整 Map 大小和选择负载因子涵盖了 Map 调整选项。 以下是一个用于获得最佳 Map 性能的简单方法 1.
将您的所有 Map 变量声明为 Map,而不是任何具体实现,即不要声明为 HashMap 或 Hashtable,或任何其他 Map 类实现。 Map criticalMap = new HashMap(); //好 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。 不要将 ConcurrentHashMap 包装在同步的包装器中,即使它将用于多个线程。 使用默认大小和负载因子。 3.
监测您的应用程序。 如果发现某个 Map 造成瓶颈,则分析造成瓶颈的原因,并部分或全部更改该 Map 的以下内容: Map 类;Map 大小;负载因子;关键对象 equals() 方法实现。 专用的 Map 的基本上都需要特殊用途的定制 Map 实现,否则通用 Map 将实现您所需的性能目标。 Map 选择 也许您曾期望更复杂的考量,而这实际上是否显得太容易?