2016-12-15 2 views
2

Я нашел исходный код функции изменения размера() из HashMap в jdk8:HashMap размер в jdk8

final Node<K,V>[] resize() { 
    Node<K,V>[] oldTab = table; 
    int oldCap = (oldTab == null) ? 0 : oldTab.length; 
    int oldThr = threshold; 
    int newCap, newThr = 0; 
    if (oldCap > 0) { 
     if (oldCap >= MAXIMUM_CAPACITY) { 
      threshold = Integer.MAX_VALUE; 
      return oldTab; 
     } 
     else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY && 
       oldCap >= DEFAULT_INITIAL_CAPACITY) 
      newThr = oldThr << 1; // double threshold 
    } 
    else if (oldThr > 0) // initial capacity was placed in threshold 
     newCap = oldThr; 
    else {    // zero initial threshold signifies using defaults 
     newCap = DEFAULT_INITIAL_CAPACITY; 
     newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY); 
    } 
    if (newThr == 0) { 
     float ft = (float)newCap * loadFactor; 
     newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ? 
        (int)ft : Integer.MAX_VALUE); 
    } 
    threshold = newThr; 
    ...// others are omitted 
} 

Мой вопрос в этом, если заявление:

else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY && 
       oldCap >= DEFAULT_INITIAL_CAPACITY) 
      newThr = oldThr << 1; // double threshold 

кажется, что если oldCap меньше 16, карта не будет удваивать свой порог. И я обнаружил, что, когда размер меньше, чем 16, порог удваивается в этом коде:

if (newThr == 0) { 
    float ft = (float)newCap * loadFactor; 
    newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ? 
        (int)ft : Integer.MAX_VALUE); 
} 

Какова цель дизайна, как это? Почему бы просто не написать так:

final Node<K,V>[] resize() { 
    Node<K,V>[] oldTab = table; 
    int oldCap = (oldTab == null) ? 0 : oldTab.length; 
    int oldThr = threshold; 
    int newCap, newThr = 0; 
    if (oldCap > 0) { 
     if (oldCap >= MAXIMUM_CAPACITY) { 
      threshold = Integer.MAX_VALUE; 
      return oldTab; 
     } 
     else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY) 
      newThr = oldThr << 1; //just double the threshold 
    } 
    else if (oldThr > 0) // initial capacity was placed in threshold 
     newCap = oldThr; 
    else {    // zero initial threshold signifies using defaults 
     newCap = DEFAULT_INITIAL_CAPACITY; 
     newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY); 
    } 
    threshold = newThr; 
    ...// others are omitted 
} 

Кроме того, это исходный код HashMap в JDK6:

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); 
} 
... 
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); 
} 

Большое спасибо!

+2

Реализации HashMaps, как правило, полны магических чисел и микро-оптимизаций, о которых заботятся только эксперты. Я только предполагаю, что кто-то обнаружил, что это было быстрее, чем старый код в большем количестве случаев, и поэтому они переписали его. Этот вопрос очень трудно ответить без документации тех, кто внес изменения. – 4castle

ответ

6

Похоже, что если oldCap меньше 16, карта не будет удваивать его размер.

Я думаю, что вы неправильно понимаете код:

else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY && 
      oldCap >= DEFAULT_INITIAL_CAPACITY) 
     newThr = oldThr << 1; // double threshold 

Обратите внимание на (newCap = oldCap << 1) подвыражения? Это безусловное назначение ... и удваивает емкость.

Также вы предлагаете это:

newThr = oldThr << 1; //just double the size 

Я думаю, что вам не хватает различия между мощностью и порога. Значение newThr не является «размером».

  • емкость является размер хэш-массива
  • порог это число, которое записи хэш-таблиц допускается до изменения размера срабатывает. До определенной точки порог равен capacity * loadFactor. Когда достигается максимальная емкость, порог становится практически бесконечным (представлен как Integer.MAX_VALUE).

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

И, наконец, этот код был сильно оптимизирован, и некоторые из запутанного характера кода могут быть следствием этого.

+2

Оптимизация не так уж трудно понять. Формальным определением нового порога является 'newCap * loadFactor', тогда как' loadFactor' является значением с плавающей запятой. Но когда вы удваиваете емкость, вы знаете, что новый порог будет также двойным от старого порога, поэтому вы можете сделать это с помощью целочисленной арифметики (сдвиг влево на единицу), без необходимости умножения с плавающей запятой или преобразования типов. Однако, когда числа слишком малы, вы не можете этого сделать, поскольку ошибки округления будут слишком высокими. Кроме того, вы должны заботиться о том, что новый порог не вписывается в 'int'. Это все. – Holger

+1

@ Хольгер - Да. Мой «свернутый» ярлык относительный. –

+0

@ StephenC Извините, это моя вина. «Размер» должен быть «пороговым», спасибо за указание на это. Я только что исправил это в своем вопросе. – weaver

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