有效地迭代几个Java Map键集合的迭代
在我的一个Java 6项目中,我有一个LinkedHashMap实例数组作为要遍历所有键的方法的输入(即通过所有映射的键集的并集)并使用关联的值。 并非所有的键都存在于所有的地图中,并且该方法不应该经过一次以上的每个键或者改变输入地图。
我目前的实现如下所示:
Set<Object> keyset = new HashSet<Object>();
for (Map<Object, Object> map : input) {
for (Object key : map.keySet()) {
if (keyset.add(key)) {
...
}
}
}
HashSet实例确保没有任何密钥将被多次执行。
不幸的是,这部分代码在性能方面非常关键,因为它被称为非常频繁。 实际上,根据profiler超过10%的CPU时间花费在HashSet.add()
方法中。
我正试图尽可能优化这些代码。 使用LinkedHashMap及其更有效的迭代器(与纯HashMap相比)显着提升,但我希望能够将本质上的书籍保留时间降至最低。
由于之后调用HashSet.contains()
的代价,使用addAll()
将所有键放在HashSet中效率较低。 目前,我正在研究是否可以使用位图(以及确切的boolean[]
)来完全避免HashSet,但根据我的密钥范围它可能根本不可能。
有没有更有效的方法来做到这一点? 最好是不会对密钥造成限制的东西?
编辑:
一些澄清和评论:
我确实需要地图上的所有值 - 我不能放弃他们中的任何一个。
我也需要知道每个值来自哪个映射。 我的代码中缺少的部分( ...
)是这样的:
for (Map<Object, Object> m : input) {
Object v = m.get(key);
// Do something with v
}
一个简单的例子来了解我需要如何处理这些地图,就是像这样并行地打印所有的地图:
Key Map0 Map1 Map2
F 1 null 2
B 2 3 null
C null null 5
...
这不是我正在做的,但你应该明白。
输入地图是非常可变的。 事实上,这个方法的每个调用都使用了一组不同的方法。 因此,我不会通过缓存键的联合来获得任何东西。
我的密钥都是String实例。 他们使用单独的HashMap在堆上进行排序,因为它们非常重复,因此它们的哈希代码已经被缓存,并且大部分哈希验证(当HashMap实现正在检查两个密钥实际上是否相等,在哈希代码之后匹配)归结为一个身份比较( ==
)。 分析器确认只有0.5%的CPU时间花在了String.equals()
和String.hashCode()
。
编辑2:
根据答案中的建议,我一路做了一些测试,分析和基准测试。 我最终的表现大约增加了7%。 我做了什么:
我将HashSet的初始容量设置为所有输入地图的总大小的两倍。 通过消除HashSet中的大部分(all?) resize()
调用,这使我获得了1-2%的区域。
我使用Map.entrySet()
作为当前迭代的地图。 由于额外的代码和担心额外的检查和Map.Entry
getter方法调用会超过任何优势,我最初避免了这种方法。 事实证明,整体代码稍快。
我确信有些人会开始对我尖叫,但这里是:原始类型。 更具体地说,我在上面的代码中使用了HashSet的原始形式。 由于我已经在使用Object
作为其内容类型,所以我不会失去任何类型的安全性。 调用HashSet.add()
时,无用的checkcast
操作的代价显然非常重要,可以在删除时提高4%的性能。 为什么JVM坚持检查Object
转换超出了我的范围......
无法为您的方法提供替代方案,但有一些建议(略)优化现有代码。
keySet()
因为它总是会在后台创建一个新集合。 使用entrySet()
,应该快得多 equals()
和hashCode()
- 如果它们“很贵”,那么你对add
方法有负面影响。 你如何避免使用HashSet取决于你在做什么。
每次input
改变时,我只计算一次联合。 这与查询次数相比应该是相对罕见的。
// on an update.
Map<Key, Value> union = new LinkedHashMap<Key, Value>();
for (Map<Key, Value> map : input)
union.putAll(map);
// on a lookup.
Value value = union.get(key);
// process each key once
for(Entry<Key, Value> entry: union) {
// do something.
}
选项A是使用.values()方法并遍历它。 但我想你已经想到了。
如果代码经常被调用,那么可能值得创建额外的结构(取决于数据更改的频率)。 创建一个新的HashMap; 你的任何hashmaps中的每个关键字都是这个关键字,并且这个关键字出现在该列表中。
如果数据有点静态(与查询频率有关),这将有所帮助,因此管理结构的过载相对较小,并且如果密钥空间不是非常密集(密钥在不同的HashMaps中不会重复很多) ,因为它会节省很多不需要的contains()。
当然,如果你要混合数据结构,最好是将所有数据封装在你自己的数据结构中。
链接地址: http://www.djcxy.com/p/8097.html上一篇: Iterating through the union of several Java Map key sets efficiently