Класс Java ConcurrentHashMap является частью Классов Коллекций для многозадачности. Это реализация хэш-таблицы, поддерживающая одновременное извлечение и обновление. Он используется в многозадачной среде для избежания ConcurrentModificationException.
ConcurrentHashMap
Если мы пытаемся изменить коллекцию во время итерации по ней, мы получаем ConcurrentModificationException
. В Java 1.5 были введены конкурентные классы в пакете java.util.concurrent
, чтобы преодолеть этот сценарий. ConcurrentHashMap – это реализация Map, которая позволяет нам изменять Map во время итерации. Операции 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
Map<String,String> myMap = new ConcurrentHashMap<String,String>();
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<String> 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<String,String>();
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<String> 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, но итератор завершился неудачно. Фактически, итератор на объектах Collection является fail-fast, т.е. любое изменение в структуре или количестве записей в объекте коллекции вызовет исключение.
Как итератор узнает о модификации в коллекции?
Мы взяли набор ключей из 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.
Вы можете просмотреть больше примеров коллекций Java из нашего Репозитория GitHub.
Ссылка: Документация по API
Source:
https://www.digitalocean.com/community/tutorials/concurrenthashmap-in-java