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

Лучшая начальная емкость HashMap при индексировании списка

У меня есть список (List<T> list), и я хочу индексировать его объекты своими идентификаторами, используя карту (HashMap<Integer, T> map). Я всегда использую list.size() как начальную емкость в конструкторе HashMap, как в приведенном ниже коде. Является ли это лучшей начальной способностью, которая будет использоваться в этом случае?

Примечание. Я никогда не добавлю больше элементов на карту.

List<T> list = myList;
Map<Integer, T> map = new HashMap<Integer, T>(list.size());
for(T item : list) {
    map.put(item.getId(), item);
}
4b9b3361

Ответ 1

Если вы не хотите перефразировать HashMap, и знаете, что в HashMap не будут помещены другие элементы, тогда вы должны учитывать коэффициент загрузки, а также начальную емкость. Коэффициент загрузки для параметра HashMap по умолчанию 0,75.

Расчет, чтобы определить, требуется ли повторная регистрация, возникает всякий раз, когда добавляется новая запись, например. put помещает новый ключ/значение. Поэтому, если вы укажете начальную емкость list.size() и коэффициент загрузки 1, то она будет перефразироваться после последнего put. Поэтому, чтобы предотвратить повторное использование, используйте коэффициент нагрузки 1 и емкость list.size() + 1.

ИЗМЕНИТЬ

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

HashMap<Integer, T> map = new HashMap<Integer, T>(list.size(), 1.0);

Вот соответствующий фрагмент исходного кода HashMap:

void addEntry(int hash, K key, V value, int bucketIndex) {
    Entry<K,V> e = table[bucketIndex];
    table[bucketIndex] = new Entry<>(hash, key, value, e);
    if (size++ >= threshold)
        resize(2 * table.length);
}

Ответ 2

Ключевое слово 'capacity' неверно по определению и не используется в обычном режиме.

По умолчанию "коэффициент загрузки" HashMap равен 0,75, это означает, что, когда количество записей в HashMap достигает 75% от поставляемой емкости, оно изменит размер массива и перефразирует.

Например, если я делаю:

Map<Integer, Integer> map = new HashMap<>(100);

Когда я добавляю 75-ю запись, карта изменит размер таблицы ввода на 2 * map.size() (или 2 * table.length). Поэтому мы можем сделать несколько вещей:

  • Измените коэффициент загрузки - это может повлиять на производительность карты.
  • Задайте начальную емкость для list.size()/0.75 + 1

Лучший вариант - последний из двух, позвольте мне объяснить, что происходит здесь:

list.size() / 0.75

Это вернет list.size() + 25% от list.size(), например, если мой список имел размер 100, он вернет 133. Затем мы добавим 1 к нему, поскольку карта будет изменена, если размер составляет 75% от начальной емкости, поэтому, если бы у нас был список размером 100, мы бы установили начальную емкость 134, это означало бы, что добавление всех 100 записей из списка не приведет к изменению размера карту.

Конечный результат:

Map<Integer, Integer> map = new HashMap<>(list.size() / 0.75 + 1);

Ответ 3

То, что вы делаете, прекрасно. Таким образом, вы уверены, что хэш-карта имеет как минимум достаточную емкость для начальных значений. Если у вас есть дополнительная информация о шаблонах использования хеш-карты (пример: часто ли она обновляется? Часто добавляются много новых элементов?), Вы можете установить большую начальную емкость (например, list.size() * 2), но никогда ниже. Используйте профилировщик, чтобы определить, скоро ли начальная мощность падает слишком быстро.

UPDATE

Благодаря @PaulBellora для указания, что начальная емкость должна быть установлена ​​на (int)Math.ceil(list.size() / loadFactor) (обычно коэффициент загрузки по умолчанию равен 0,75), чтобы избежать первоначального изменения размера.

Ответ 4

Guava Maps.newHashMapWithExpectedSize использует этот вспомогательный метод для расчета начальной емкости для коэффициента загрузки по умолчанию 0.75 на основе некоторого ожидаемого количества значений:

/**
 * Returns a capacity that is sufficient to keep the map from being resized as
 * long as it grows no larger than expectedSize and the load factor is >= its
 * default (0.75).
 */
static int capacity(int expectedSize) {
    if (expectedSize < 3) {
        checkArgument(expectedSize >= 0);
        return expectedSize + 1;
    }
    if (expectedSize < Ints.MAX_POWER_OF_TWO) {
        return expectedSize + expectedSize / 3;
    }
    return Integer.MAX_VALUE; // any large value
}

ссылка: источник

Из документации newHashMapWithExpectedSize:

Создает экземпляр HashMap с достаточно высокой "начальной емкостью", которая он должен содержать элементы expectedSize без роста. Это поведение не может быть в целом гарантирована, но это справедливо для OpenJDK 1.6. Также не может быть гарантировано, что метод не непреднамеренно увеличивая размер возвращаемой карты.

Ответ 5

В соответствии с справочной документацией java.util.HashMap:

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

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

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

Ответ 6

Эмпирическое правило, если вы не знаете внутреннее значение коэффициента загрузки/емкости:

initialCapacityToUse = (Expected No. of elements in map / 0.75) + 1

При этом начальном значении емкости перефразировка не будет происходить для хранения с ожидаемым значением no. элементов в карте.