查找错误警告:低效使用键集迭代器而不是条目集迭代器

2022-09-01 04:57:11

请参考以下方法:

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 条目的值。在 map 的 entrySet 上使用迭代器会更有效,以避免 Map.get(key) 查找。


答案 1

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

您可以循环访问映射以获取映射条目 (Map.Entry)(几个键和值),并且只能访问一次映射。

Map.entrySet() 提供一组 s,每个 s 都有键和相应的值。Map.Entry

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

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


答案 2

如果有人仍然对详细且有数字支持的答案感兴趣:是的,您应该使用vs。 如果您正在迭代整个地图。有关详细数字,请参阅此要点。我使用JMH运行了一个基准测试,用于使用Oracle JDK8的Map的默认实现。entrySet()keySet()

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

但是,这些仍然是非常小的数字。迭代超过100万个条目的最慢方法是使用 ,大约500-700毫秒;而迭代只是25-30毫秒,后面是40-50毫秒(这并不奇怪,因为它里面有一个,这有助于迭代)。作为上面链接的要点的概述:ConcurrentSkipListMap.keySet()IdentityHashMap.entrySet()LinkedHashMap.entrySet()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

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

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


推荐