36

请参考以下方法:

public Set<LIMSGridCell> getCellsInColumn(String columnIndex){
    Map<String,LIMSGridCell> cellsMap = getCellsMap();
    Set<LIMSGridCell> cells = new HashSet<LIMSGridCell>();
    Set<String> keySet = cellsMap.keySet();
    for(String key: keySet){
      if(key.startsWith(columnIndex)){
        cells.add(cellsMap.get(key));
      }
    }
    return cells;
  }

FindBugs 给出这个警告信息:

"低效使用 keySet 迭代器而不是 entrySet 迭代器 此方法使用从 keySet 迭代器检索到的键来访问 Map 条目的值。在映射的 entrySet 上使用迭代器更有效,以避免 Map .get(key) 查找。”

4

5 回答 5

57

您正在检索所有键(访问整个映射),然后对于某些键,您再次访问映射以获取值。

您可以遍历地图以获取地图条目(Map.Entry)(一对键和值)并且只访问一次地图。

Map.entrySet()提供一组Map.Entrys,每个都带有键和对应的值。

for ( Map.Entry< String, LIMSGridCell > entry : cellsMap.entrySet() ) {
    if ( entry.getKey().startsWith( columnIndex ) ) {
        cells.add( entry.getValue() );
    }
}

注意:我怀疑这将是一个很大的改进,因为如果您使用映射条目,您将为每个条目实例化一个对象。我不知道这是否真的比get()直接调用和检索所需的参考更快。

于 2012-09-28T11:38:44.970 回答
13

如果有人仍然对详细且有数字支持的答案感兴趣:是的,您应该使用entrySet()vs.keySet()以防您遍历整个地图。有关详细数字,请参阅此 Gist。我使用 JMH 为使用 Oracle JDK8 的 Map 的默认实现运行基准测试。

主要发现是:迭代keySet和重新查询每个键总是有点慢。一旦你有更大的地图,乘数就会变得相当大(例如,对于 aConcurrentSkipListMap它总是 5-10x;而对于HashMaps 它不大于 2x 最多有一百万个条目)。

然而,这些仍然是非常小的数字。迭代超过 100 万个条目的最慢方法是使用 a ConcurrentSkipListMap.keySet(),大约为 500-700 毫秒;而迭代IdentityHashMap.entrySet()只是 25-30 毫秒,LinkedHashMap.entrySet()紧随其后的是 40-50 毫秒(不足为奇,因为它有一个LinkedList内部,这有助于迭代)。作为上述链接要点的概述:

Map type              | Access Type | Δ for 1M entries
----------------------+-------------+-----------------
HashMap               | .entrySet() |     69-72  ms
HashMap               |   .keySet() |     86-94  ms
ConcurrentHashMap     | .entrySet() |     72-76  ms
ConcurrentHashMap     |   .keySet() |     87-95  ms
TreeMap               | .entrySet() |    101-105 ms
TreeMap               |   .keySet() |    257-279 ms
LinkedHashMap         | .entrySet() |     37-49  ms
LinkedHashMap         |   .keySet() |     89-120 ms
ConcurrentSkipListMap | .entrySet() |     94-108 ms
ConcurrentSkipListMap |   .keySet() |    494-696 ms
IdentityHashMap       | .entrySet() |     26-29  ms
IdentityHashMap       |   .keySet() |     69-77  ms

所以底线是:这取决于您的用例。虽然迭代数字并不大,特别是对于相当小的地图来说,迭代肯定更快。entrySet()但是,如果您经常迭代具有 100 万个条目的 Map,最好使用更快的方法;)

当然,这些数字只是为了相互比较,而不是绝对的。

于 2016-06-30T07:09:36.957 回答
9

您正在获取地图中的一组键,然后使用每个键从地图中获取值。

相反,您可以简单地遍历通过.Entry返回给您的Map.EntryentrySet()键/值对。这样你就可以避免相对昂贵的get()查找(注意这里使用了相对这个词)

例如

for (Map.Entry<String,LIMSGridCell> e : map.entrySet()) {
   // do something with...
   e.getKey();
   e.getValue();
}
于 2012-09-28T11:38:45.067 回答
2

这是建议;不是你的问题的真正答案。当您使用 ConcurrentHashMap 时;下面是javadoc中提到的迭代器行为

视图的迭代器是一个“弱一致”的迭代器,它永远不会抛出 ConcurrentModificationException,并保证在构造迭代器时遍历元素,并且可以(但不保证)反映构造后的任何修改。

所以如果你使用 EntrySet 迭代器;这可能包含陈旧的键/值对;所以会更好;从 keySet iterator() 获取密钥;并检查收藏价值。这将确保您从集合中获得最近的更改。

如果您对故障安全迭代器没问题;然后检查此链接;它使用 entrySet 声明;对性能的改善不大。

于 2014-06-26T08:21:35.860 回答
0

在 keyset 中,您需要获取所有键,然后搜索集合中的每个键。

此外,遍历 entrySet 更快,因为您不会为每个键查询两次映射。

如果您只需要 Map 的键或值,请使用 keySet() 或 values()。

于 2015-05-14T06:54:32.887 回答