Что происходит, когда достигается максимальная емкость HashMap или HashSet?

Всего несколько минут назад я ответил на вопрос о максимальном возможном размере HashMap в Java. Как я всегда читал, HashMap - это растущая структура данных. Этот размер ограничен только размером памяти JVM. Поэтому я думал, что нет жесткого ограничения его размера и соответственно ответил. (То же самое применимо и к HashSet.)

Но кто-то исправил меня, сказав, что, поскольку метод size() HashMap возвращает int, существует ограничение на его размер. Совершенно правильная точка. Я просто попытался протестировать его на локальном компьютере, но не смог, мне нужно больше, чем 8 ГБ памяти, чтобы вставить более 2 147 483 64де целых чисел в HashMap, которых у меня нет.

Мои вопросы были:

  • Что происходит, когда мы пытаемся вставить 2 147 473 647 + 1 элемент в HashMap/HashSet?
  • Есть ли ошибка?
  • Если да, какая ошибка? Если не то, что происходит с HashMap/HashSet, то уже существующих элементов и нового элемента?

Если кто-то получает доступ к машине с 16 ГБ памяти, вы можете попробовать ее практически.:)

Ответы

Ответ 1

Базовая емкость массива должна быть равна 2 (которая ограничена 2 ^ 30). Когда этот размер достигнут, коэффициент загрузки эффективно игнорируется, и массив перестает расти.

В этот момент скорость столкновений увеличивается.

Учитывая, что hashCode() имеет только 32 бита, не имеет смысла расти намного больше, чем это в любом случае.

/**
 * Rehashes the contents of this map into a new array with a
 * larger capacity.  This method is called automatically when the
 * number of keys in this map reaches its threshold.
 *
 * If current capacity is MAXIMUM_CAPACITY, this method does not
 * resize the map, but sets threshold to Integer.MAX_VALUE.
 * This has the effect of preventing future calls.
 *
 * @param newCapacity the new capacity, MUST be a power of two;
 *        must be greater than current capacity unless current
 *        capacity is MAXIMUM_CAPACITY (in which case value
 *        is irrelevant).
 */
void resize(int newCapacity) {
    Entry[] oldTable = table;
    int oldCapacity = oldTable.length;
    if (oldCapacity == MAXIMUM_CAPACITY) {
        threshold = Integer.MAX_VALUE;
        return;
    }

    Entry[] newTable = new Entry[newCapacity];
    transfer(newTable);
    table = newTable;
    threshold = (int)(newCapacity * loadFactor);
}

Когда размер превышает Integer.MAX_VALUE, он переполняется.

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