HashMap поддерживает дубликаты ключей

Во время экспериментов с HashMap я заметил что-то странное.

Ran 4 streamа с каждой попыткой поставить (ключ, значение) с ключами от 0 до 9999, значение константной строки. После того, как все streamи были выполнены, map.size() вернул значение больше 10000. Как это произошло? Означает ли это, что карта содержит дубликаты ключей?

Я повторил на map.entrySet() и обнаружил, что количество для некоторых ключей действительно было больше 1. Какое значение будет возвращено, если я сделаю get() на карте для одного такого ключа.

Вот код, который я пробовал

 final HashMap vals = new HashMap(16_383); Runnable task = new Runnable() { @Override public void run() { for (int i = 0; i < 10000; i++) { vals.put(""+i, Thread.currentThread().getName()); } } }; Thread thread = new Thread(task, "a"); Thread thread1 = new Thread(task, "b"); Thread thread2 = new Thread(task, "c"); Thread thread3 = new Thread(task, "d"); thread.start(); thread1.start(); thread2.start(); thread3.start(); thread.join(); thread1.join(); thread2.join(); thread3.join(); System.out.println(Thread.currentThread().getName() + "vals "+ vals.size()); System.out.println(Thread.currentThread().getName() + "vals "+ vals.entrySet().size()); System.out.println(Thread.currentThread().getName() + "vals "+ vals.keySet().size()); 

HashMap не является streamобезопасным, как это явно указано в связанных документах. Вы являетесь хорошим примером того, почему это так. Да, вы put дубликаты ключей, потому что put не проверяет, что другой stream помещает один и тот же ключ. Вот что значит не быть streamобезопасным.

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

Есть несколько обходных решений. В документах, предложенных в

Map m = Collections.synchronizedMap(new HashMap(...));

Другой вариант – использовать ConcurrentHashMap , который явно разработан для этой цели.