Подтвердить что ты не робот

Предупреждение FindBugs: неэффективное использование итератора keySet вместо итератора entrySet

Обратитесь к следующему методу:

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. Эффективнее использовать iterator на entrySet карты, чтобы избежать Map.get(key) поиск".

4b9b3361

Ответ 1

Вы извлекаете все ключи (доступ ко всей карте), а затем для некоторых клавиш, вы снова получаете доступ к карте, чтобы получить значение.

Вы можете перебирать карту, чтобы получить записи в карте (Map.Entry) (пары ключей и значений) и получить доступ к карте только один раз.

Map.entrySet() предоставляет набор Map.Entry с каждым ключом и соответствующим значением.

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

Примечание. Я сомневаюсь, что это будет большим улучшением, поскольку, если вы используете записи в карте, вы создадите экземпляр объекта для каждой записи. Я не знаю, действительно ли это происходит быстрее, чем вызов get() и непосредственное получение необходимой ссылки.

Ответ 2

Вы получаете набор ключей на карте, а затем используете каждую клавишу, чтобы получить значение из карты.

Вместо этого вы можете просто прокрутить через пары Map.Entry ключ/значение, возвращенный вам через entrySet(). Таким образом, вы избегаете относительно дорогого поиска get() (обратите внимание на использование слова относительно здесь)

например.

for (Map.Entry<String,LIMSGridCell> e : map.entrySet()) {
   // do something with...
   e.getKey();
   e.getValue();
}

Ответ 3

Если кто-то все еще интересуется подробным и подтвержденным числом ответов: да, вы должны использовать entrySet() vs. keySet() в случае, если вы повторяете всю карту. См. этот Gist для подробных номеров. Я запускаю тест с JMH для реализации по умолчанию Map с Oracle JDK8.

Основной вывод: он всегда немного медленнее перебирать по keySet и повторно запрашивать для каждого ключа. Как только у вас будут большие карты, множитель может стать довольно большим (например, для ConcurrentSkipListMap он всегда 5-10x, а для HashMap он не больше 2x для до миллиона записей).

Однако они все еще очень маленькие. Самый медленный способ перебора более 1 миллиона записей - с ConcurrentSkipListMap.keySet(), который составляет 500-700 миллисекунд; а итерация более IdentityHashMap.entrySet() составляет всего 26-28 миллисекунд. Чтобы привести примеры более широко используемых карт: ConcurrentHashMap.entrySet() 72-74 ms ConcurrentHashMap.keySet() 87-93 ms HashMap.entrySet() 68-70 ms HashMap.keySet() 86-92 ms TreeMap.entrySet() 100-104 ms TreeMap.keySet() 257-277 ms

Итак, нижняя строка: это зависит от вашего прецедента. В то время как для entrySet() итераций быстрее цифры не огромны, особенно для разумно малых карт. Однако, если вы регулярно повторяете карту с 1 миллионом записей, лучше используйте более быстрый способ;)

Числа, конечно, просто для сравнения друг с другом, а не с абсолютами.

Ответ 4

Это предложение; на самом деле не ответ на ваш вопрос. Когда вы работаете с ConcurrentHashMap; ниже показано поведение итератора, указанное в javadoc

Итератор представления - это "слабо согласованный" итератор, который никогда не будет бросить ConcurrentModificationException и гарантировать элементов, как они существовали при построении итератора, и может (, но не гарантируется), отражают любые изменения после строительство.

Итак, если вы используете итератор EntrySet; это может содержать устаревшую пару ключ/значение; так что было бы лучше; получить ключ от keySet iterator(); и проверьте с коллекцией значение. это гарантирует, что вы получите последнее изменение из коллекции.

Если вы в порядке с итератором с отказоустойчивостью; затем установите ссылку ; он указывает, используя entrySet; мало улучшая производительность.

Ответ 5

В наборе ключей вам нужно получить все ключи, а затем выполнить поиск каждого ключа в коллекции.

Кроме того, цикл над entrySet выполняется быстрее, потому что вы не запрашиваете карту дважды для каждого ключа.

Если вам нужны только ключи или только значения вашей Карты, используйте скорее keySet() или значения().