Автор оригинала: Pankaj Kumar.
Класс Java ConcurrentHashMap является частью классов коллекции параллелизма. Это реализация хэш – таблицы, которая поддерживает одновременный поиск и обновления. Он используется в многопоточной среде, чтобы избежать исключения ConcurrentModificationException .
Карта совпадений
Если мы попытаемся изменить коллекцию во время итерации по ней, мы получим ConcurrentModificationException
. Java 1.5 представила параллельные классы в пакете java.util.concurrent
, чтобы преодолеть этот сценарий. ConcurrentHashMap-это реализация карты, которая позволяет нам изменять карту во время итерации. Операции ConcurrentHashMap являются потокобезопасными. ConcurrentHashMap не допускает значение null для ключей и значений.
Пример Java ConcurrentHashMap
Класс ConcurrentHashMap
аналогичен классу HashMap , за исключением того, что он потокобезопасен и допускает модификацию во время итерации.
package com.journaldev.util; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; public class ConcurrentHashMapExample { public static void main(String[] args) { //ConcurrentHashMap MapmyMap = new ConcurrentHashMap (); myMap.put("1", "1"); myMap.put("2", "1"); myMap.put("3", "1"); myMap.put("4", "1"); myMap.put("5", "1"); myMap.put("6", "1"); System.out.println("ConcurrentHashMap before iterator: "+myMap); Iterator it = myMap.keySet().iterator(); while(it.hasNext()){ String key = it.next(); if(key.equals("3")) myMap.put(key+"new", "new3"); } System.out.println("ConcurrentHashMap after iterator: "+myMap); //HashMap myMap = new HashMap (); myMap.put("1", "1"); myMap.put("2", "1"); myMap.put("3", "1"); myMap.put("4", "1"); myMap.put("5", "1"); myMap.put("6", "1"); System.out.println("HashMap before iterator: "+myMap); Iterator it1 = myMap.keySet().iterator(); while(it1.hasNext()){ String key = it1.next(); if(key.equals("3")) myMap.put(key+"new", "new3"); } System.out.println("HashMap after iterator: "+myMap); } }
Вывод :
ConcurrentHashMap before iterator: {1=1, 5=1, 6=1, 3=1, 4=1, 2=1} ConcurrentHashMap after iterator: {1=1, 3new=new3, 5=1, 6=1, 3=1, 4=1, 2=1} HashMap before iterator: {3=1, 2=1, 1=1, 6=1, 5=1, 4=1} Exception in thread "main" java.util.ConcurrentModificationException at java.util.HashMap$HashIterator.nextEntry(HashMap.java:793) at java.util.HashMap$KeyIterator.next(HashMap.java:828) at com.test.ConcurrentHashMapExample.main(ConcurrentHashMapExample.java:44)
Из выходных данных ясно, что ConcurrentHashMap обрабатывает новую запись на карте во время итерации, в то время как HashMap выдает ConcurrentModificationException
.
Давайте внимательно рассмотрим трассировку стека исключений. Следующее утверждение вызвало исключение.
String key = it1.next();
Это означает, что новая запись была вставлена в хэш-карту, но итератор не работает. На самом деле, итератор для объектов коллекции быстр , т. е. Любое изменение структуры или количества записей в объекте коллекции вызовет исключение.
Как итератор узнает об изменении в коллекции?
Мы взяли набор ключей из HashMap, а затем повторили его.
HashMap содержит переменную для подсчета количества модификаций, и итератор использует ее при вызове функции next() для получения следующей записи.
HashMap.java
:
/** * The number of times this HashMap has been structurally modified * Structural modifications are those that change the number of mappings in * the HashMap or otherwise modify its internal structure (e.g., * rehash). This field is used to make iterators on Collection-views of * the HashMap fail-fast. (See ConcurrentModificationException). */ transient volatile int modCount;
Давайте немного изменим код, чтобы выйти из цикла итератора при вставке новой записи. Все, что нам нужно сделать, это добавить инструкцию break после вызова put.
if(key.equals("3")){ myMap.put(key+"new", "new3"); break; }
Вывод с приведенным выше кодом:
ConcurrentHashMap before iterator: {1=1, 5=1, 6=1, 3=1, 4=1, 2=1} ConcurrentHashMap after iterator: {1=1, 3new=new3, 5=1, 6=1, 3=1, 4=1, 2=1} HashMap before iterator: {3=1, 2=1, 1=1, 6=1, 5=1, 4=1} HashMap after iterator: {3=1, 2=1, 1=1, 3new=new3, 6=1, 5=1, 4=1}
Что произойдет, если значение ключа будет изменено?
Что, если мы не добавим новую запись, а обновим существующую пару ключ-значение?
Будет ли это вызывать исключение?
Давайте изменим код в исходной программе и проверим его.
//myMap.put(key+"new", "new3"); myMap.put(key, "new3");
Никаких исключений не будет, потому что коллекция будет изменена, но ее структура останется прежней.
Дальнейшее Чтение
Вы заметили эти угловые скобки при создании нашего объекта коллекции и итератора?
Он называется универсальными и очень эффективен, когда дело доходит до проверки типов во время компиляции, чтобы удалить исключение ClassCastException во время выполнения. Узнайте больше о дженериках в примере дженериков Java .
Вы также должны прочитать Вопросы для интервью с коллекциями Java и шаблон проектирования итератора на Java .
Ссылка: API Doc