У HashMap
есть такая фраза из него:
Если начальная емкость больше максимального количества записей, деленная на коэффициент нагрузки, никаких операций перефразирования никогда не произойдет.
Обратите внимание на то, как документация говорит о переименовании, а не изменении размера - даже если переименование произойдет только тогда, когда будет изменен размер; то есть когда внутренний размер ведер становится вдвое большим.
И, конечно же, HashMap
предоставляет такой конструктор, где мы могли бы определить эту начальную емкость.
Создает пустой HashMap с указанной начальной мощностью и коэффициентом загрузки по умолчанию (0.75).
Хорошо, кажется достаточно простым:
// these are NOT chosen randomly...
List<String> list = List.of("DFHXR", "YSXFJ", "TUDDY",
"AXVUH", "RUTWZ", "DEDUC", "WFCVW", "ZETCU", "GCVUR");
int maxNumberOfEntries = list.size(); // 9
double loadFactor = 0.75;
int capacity = (int) (maxNumberOfEntries / loadFactor + 1); // 13
Таким образом, емкость составляет 13
(внутренне это 16
- следующая мощность двух), таким образом мы гарантируем, что часть документации не будет содержать никаких повторов. Хорошо давайте протестировать это, но сначала введем метод, который войдет в HashMap
и посмотрит на значения:
private static <K, V> void debugResize(Map<K, V> map, K key, V value) throws Throwable {
Field table = map.getClass().getDeclaredField("table");
table.setAccessible(true);
Object[] nodes = ((Object[]) table.get(map));
// first put
if (nodes == null) {
// not incrementing currentResizeCalls because
// of lazy init; or the first call to resize is NOT actually a "resize"
map.put(key, value);
return;
}
int previous = nodes.length;
map.put(key, value);
int current = ((Object[]) table.get(map)).length;
if (previous != current) {
++HashMapResize.currentResizeCalls;
System.out.println(nodes.length + " " + current);
}
}
А теперь давайте протестируем это:
static int currentResizeCalls = 0;
public static void main(String[] args) throws Throwable {
List<String> list = List.of("DFHXR", "YSXFJ", "TUDDY",
"AXVUH", "RUTWZ", "DEDUC", "WFCVW", "ZETCU", "GCVUR");
int maxNumberOfEntries = list.size(); // 9
double loadFactor = 0.75;
int capacity = (int) (maxNumberOfEntries / loadFactor + 1);
Map<String, String> map = new HashMap<>(capacity);
list.forEach(x -> {
try {
HashMapResize.debugResize(map, x, x);
} catch (Throwable throwable) {
throwable.printStackTrace();
}
});
System.out.println(HashMapResize.currentResizeCalls);
}
Ну, resize
было вызвано и, следовательно, записи, в которых повторялось, а не документация.
Как сказано, ключи не были выбраны случайным образом. Они были настроены так, чтобы они static final int TREEIFY_THRESHOLD = 8;
свойство - когда ковш преобразуется в дерево. Ну не очень, так как нам нужно также MIN_TREEIFY_CAPACITY = 64
для появления дерева; пока не произойдет resize
, или ковш удвоится по размеру; таким образом происходит перестановка записей.
Я могу только намекнуть на то, почему в этом предложении неверна документация HashMap
, поскольку до java-8 ведро не было преобразовано в Дерево; таким образом, свойство будет сохраняться, начиная с java-8 и далее, что не соответствует действительности. Поскольку я не уверен в этом, я не добавляю это в качестве ответа.