2013-08-30 2 views
6

Я хотел бы сделать что-то, используя значение карты для заданного ключа, только если карта содержит данный ключ. Наивно я хотел бы написать:Результат поиска карты

Map<String, String> myMap = ...; 

if(myMap.containsKey(key)) { 
    String value = myMap.get(key); 

    // Do things with value 
} 

код выше выглядит легко понять, но с точки зрения производительности, не было бы лучше, следующий код?

Map<String, String> myMap = ...; 

String value = myMap.get(key); 

if(value != null) { 
    // Do things with value 
} 

Во втором фрагменте мне не нравится тот факт, что value объявлен с более широкой областью.

Каким образом эффективность данных случаев изменяется в отношении реализации Карты?

Примечание. Предположим, что нулевые значения не допускаются на карту.

+1

Почему вы беспокоитесь об исполнении здесь? –

+2

@SotiriosDelimanolis Я думаю, что он просто представил простой сценарий. Представьте, что следующий код будет выполнен в цикле с миллионом итераций. –

+0

Если вы не делаете это миллионы раз, или это какая-то горячая точка, я бы не стал беспокоиться о хите производительности. – Yuushi

ответ

6

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

Для TreeMap и HashMap, например, containsKey по существу только get операции (более конкретно getEntry) с проверкой на null.

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

Обратите внимание, что HashMap.get является O (1) (с хеш-функцией, хорошо подходящей для данных) и TreeMap.get является O (log n). Поэтому, если вы выполняете какое-либо значительное количество работы в цикле, а Map не содержит порядка миллионов элементов, разница в производительности, вероятно, будет незначительной.

Однако, обратите внимание на отказ от ответственности в the docs для Map.get:

Если эта карта позволяет нулевые значения, то возвращаемое значение NULL не обязательно означает, что карта не содержит отображения для ключа; также возможно, что карта явно отображает ключ в значение null. Операция containsKey может использоваться для различения этих двух случаев.

+3

Вы начали хорошо, но, пожалуйста, подробно расскажите о различии между TreeMap, который является O (log n) и HashMap, который является O (1). С помощью HashMap вычисляется хэш-код ключа, который дает вам номер ведра.Эти операции не зависят от количества элементов. С TreeMap анализируется корневой узел, и для каждого несоответствия половина дерева отбрасывается из возможных сопоставимых элементов. Они выполняют по-разному для поиска, но иногда более медленный поиск лучше из-за лучшей производительности в других областях. –

+0

@EdwinBuck Сделал некоторые изменения. Хотя я на самом деле не намереваюсь ответить на этот вопрос, чтобы сосредоточиться на различии между TreeMap и HashMap (OP не указывал на это, и я уверен, что уже есть более чем достаточно таких анализов), более просто разница между двумя предоставили образцы кода. – Dukeling

+0

Понял, и да, различия в производительности O хорошо рекламируются во многих местах; но, учитывая необходимость объяснения того, что Map является интерфейсом, с множеством возможных реализаций, я ошибался на стороне осторожности и думал, что, возможно, он не был осведомлен о записи большого О (просто упомянул, что это тоже вводная точка входа). –

1

Очевидно, что вторая версия более эффективна: вы только просматриваете ключ на карте один раз, в то время как в первой версии вы дважды просматриваете его, следовательно, вычисляя дважды хэш-код ключа и просматривая хэш-букеты, предполагая, что вы используете хэш-карта, конечно.

У вас может быть совершенно другая реализация интерфейса карты, которая могла бы лучше обрабатывать этот вид кода, если вспомнить запись карты, которая была связана с ключом в последнем, содержит вызов метода, если последующий get использует тот же ключ (используя оператор ==), вы можете сразу же вернуть связанное значение из запоминаемой записи карты.

Однако существует опасность, во 2-й метод: что, если бы это на карте:

map.put("null", null); 

затем map.get («нулевой») возвращает нуль, и вы бы рассматривать его как «нулевой "не отображается, а map.contains (" null ") возвращает true!

+0

Вторая проблема - это только проблема, если вы разрешаете 'null' находиться на карте. – cHao

1

Чтобы ответить на ваш вопрос,
«Как производительность данных случаях изменится в отношении реализации карт?»
Разница в производительности незначительна.

Чтобы прокомментировать ваш комментарий,
«Во втором фрагменте мне не нравится тот факт, что значение объявленное с более широким размахом.»
Хорошо, вы не должны. Вы видите, есть два способа, чтобы получить нулевой возвращаемые из карты:

  1. Ключ не существует ИЛИ
  2. Ключ действительно существует, но его значение равно нулю (если реализация Map позволяет нулевой значения, такие как HashMap).

Таким образом, два сценария могут иметь разные результаты, если ключ существовал с нулевым значением!

EDIT

Я написал следующий код, чтобы проверить производительность двух сценариев:

public class TestMapPerformance { 

    static Map<String, String> myMap = new HashMap<String, String>(); 
    static int iterations = 7000000; 

    // populate a map with seven million strings for keys 
    static { 
     for (int i = 0; i <= iterations; i++) { 
      String tryIt = Integer.toString(i); 
      myMap.put(tryIt, "hi"); 
     } 
    } 
    // run each scenario twice and print out the results. 
    public static void main(String[] args) { 
     System.out.println("Key Exists: " + testMap_CheckIfKeyExists(iterations)); 
     System.out.println("Value Null: " + testMap_CheckIfValueIsNull(iterations)); 
     System.out.println("Key Exists: " + testMap_CheckIfKeyExists(iterations)); 
     System.out.println("Value Null: " + testMap_CheckIfValueIsNull(iterations)); 
    } 

    // Check if the key exists, then get its value 
    public static long testMap_CheckIfKeyExists(int iterations) {  
     Date date = new Date(); 
     for (int i = 0; i <= iterations; i++) { 
      String key = Integer.toString(i); 
      if(myMap.containsKey(key)) { 
       String value = myMap.get(key); 
       String newString = new String(value); 
      } 
     } 
     return new Date().getTime() - date.getTime(); 
    } 

    // Get the key's value, then check if that value is null 
    public static long testMap_CheckIfValueIsNull(int iterations) { 
     Date date = new Date(); 
     for (int i = 0; i <= iterations; i++) { 
      String key = Integer.toString(i); 
      String value = myMap.get(key); 
      if(value != null) { 
       String newString = new String(value); 
      } 
     } 
     return new Date().getTime() - date.getTime(); 
    } 

} 

Я выбежала его, и это было результатом:

Key Exists: 9901 
Value Null: 11472 
Key Exists: 11578 
Value Null: 9387 

Так в заключение, разница в производительности в незначительном.

Смежные вопросы