Мне сложно понять деталь реализации из java-9 ImmutableCollections.SetN
; в частности, почему требуется увеличить внутренний массив дважды.
Предположим, вы это сделали:
Set.of(1,2,3,4) // 4 elements, but internal array is 8
Точнее, я прекрасно понимаю, почему это делается (двойное расширение) в случае HashMap
- где вы никогда (почти) не хотите, чтобы load_factor
был одним. Значение !=1
улучшает время поиска, поскольку записи, например, лучше распределяются на ведра.
Но в случае непреложного Сета - я не могу сказать. Тем более, что выбирается индекс внутреннего массива.
Позвольте мне представить некоторые подробности. Сначала, как выполняется поиск индекса:
int idx = Math.floorMod(pe.hashCode() ^ SALT, elements.length);
pe
- это фактическое значение, которое мы помещаем в набор. SALT
составляет всего 32 бита, сгенерированных при запуске, один раз за JVM
(это фактическая рандомизация, если вы хотите). elements.length
для нашего примера - 8
(4 элемента, но здесь 8 - двойной размер).
Это выражение похоже на отрицательную операцию по модулю. Обратите внимание, что то же самое логическое дело выполняется в HashMap
, например ((n - 1) & hash
), когда выбран ковш.
Итак, если elements.length is 8
для нашего случая, то это выражение вернет любое положительное значение, меньшее 8 (0, 1, 2, 3, 4, 5, 6, 7)
.
Теперь остальная часть метода:
while (true) {
E ee = elements[idx];
if (ee == null) {
return -idx - 1;
} else if (pe.equals(ee)) {
return idx;
} else if (++idx == elements.length) {
idx = 0;
}
}
Позвольте сломать его:
if (ee == null) {
return -idx - 1;
Это хорошо, это означает, что текущий слот в массиве пуст - мы можем поместить наше значение там.
} else if (pe.equals(ee)) {
return idx;
Это плохо - слот занят, а уже введенная позиция равна той, которую мы хотим поставить. Set
не может иметь повторяющиеся элементы, поэтому позже вызывается Exception.
else if (++idx == elements.length) {
idx = 0;
}
Это означает, что этот слот занят (хеш-столкновение), но элементы не равны. В a HashMap
эта запись будет помещена в тот же самый ведро, что и LinkedNode
или TreeNode
, но не здесь.
Итак, index
увеличивается и выполняется следующая позиция (с небольшим предостережением, которое оно перемещается круговым способом, когда оно достигает последней позиции).
И вот вопрос: если ничего необычного (если я чего-то не хватает) выполняется при поиске индекса, почему нужно иметь массив в два раза больше? Или почему функция не была написана так:
int idx = Math.floorMod(pe.hashCode() ^ SALT, input.length);
// notice the diff elements.length (8) and not input.length (4)