Map.remove: примеры (JAVA)
Map.remove(Object key): VОписание метода Map.remove
Интерфейс java.util.Map предоставляет методы для удаления элементов по ключу. Основные варианты:
- V remove(Object key) - удаляет запись с указанным ключом и возвращает предыдущее значение, связанное с этим ключом, либо
null, если ключ отсутствовал или если связанное значение равноnull. Метод доступен с ранних версий Java. - boolean remove(Object key, Object value) - удаляет запись только в том случае, если текущая пара ключ-значение равна переданному значению (по equals). Возвращает
true, если удаление выполнено, иначеfalse. Этот метод появился в интерфейсе Map как default-метод в Java 8. Аналогичные семантики были в ConcurrentMap с момента его появления.
Аргументы и возвращаемые значения:
- Аргумент
keyпринимается какObject. Реализация использует equals и, при деревоподобных реализациях (например, TreeMap), сравнение через Comparator или Comparable. - Вариант
remove(key)возвращает значение типа V до удаления илиnull. Посколькуnullможет означать два состояния (отсутствие ключа или сохранённое null-значение), перед проверкой на отсутствие иногда применяетсяcontainsKey. - Вариант
remove(key, value)возвращаетboolean. Удаление выполняется атомарно относительно реализации Map и особенно полезно для конкурентных коллекций.
Особенности поведения на разных реализациях:
- HashMap разрешает
nullдля ключей и значений; возвращаемоеnullнужно интерпретировать с учётомcontainsKey. - ConcurrentHashMap не допускает
nullдля ключей и значений; вызов сnullприводит к NullPointerException. - TreeMap опирается на порядок, определённый Comparator/Comparable, и удаление использует сравнение ключей.
- Немодифицируемые карты (Collections.unmodifiableMap, Map.of и т. п.) выбрасывают UnsupportedOperationException при попытке удаления.
Типичные случаи использования: освобождение памяти, управление состоянием кэша, условное удаление при совпадении значения, безопасная работа в многопоточном окружении через ConcurrentMap.
Короткие примеры использования Map.remove
Пример 1. Удаление существующей записи и вывод удалённого значения.
import java.util.HashMap;
public class Main1 {
public static void main(String[] args) {
HashMap map = new HashMap<>();
map.put("a", 1);
Integer old = map.remove("a");
System.out.println(old);
System.out.println(map.containsKey("a"));
}
}
1 false
Пример 2. Удаление ключа, которого нет.
HashMap m = new HashMap<>();
String v = m.remove("missing");
System.out.println(v);
null
Пример 3. Различие между null-значением и отсутствием ключа.
HashMap m = new HashMap<>();
m.put("k", null);
System.out.println(m.remove("k"));
System.out.println(m.containsKey("k"));
null false
Пример 4. Условное удаление с двумя аргументами.
HashMap m = new HashMap<>();
m.put("x", 10);
boolean removed = m.remove("x", 10);
System.out.println(removed);
System.out.println(m.containsKey("x"));
true false
Пример 5. Попытка условного удаления при несовпадении значения.
HashMap m = new HashMap<>();
m.put("y", 5);
boolean removed = m.remove("y", 7);
System.out.println(removed);
System.out.println(m.get("y"));
false 5
Пример 6. Поведение в ConcurrentHashMap (null недопустим).
import java.util.concurrent.ConcurrentHashMap;
ConcurrentHashMap cm = new ConcurrentHashMap<>();
cm.put("k", 2);
System.out.println(cm.remove("k"));
2
Похожие методы Java и их особенности
- Map.clear() - удаляет все записи сразу; используется, когда требуется очистить коллекцию целиком вместо выборочной операции.
- Map.remove(key, value) - рассмотрен выше; предпочтителен в ситуациях, требующих условного и атомарного удаления.
- ConcurrentMap.remove(K, V) - та же семантика для конкурентных коллекций; предпочтительна при работе в многопоточной среде.
- Iterator.remove() (через entrySet().iterator()) - безопасный способ удаления при итерации по коллекции, предотвращающий ConcurrentModificationException для неконкурентных реализаций.
Выбор зависит от потребности: массовая очистка - clear(), условное удаление - remove(key, value), безопасное удаление во время обхода - удаление через итератор.
Аналоги в других языках и отличия
- JavaScript: Map.prototype.delete(key) возвращает boolean; для обычного объекта используется delete obj[key], результат - boolean. Пример:
const map = new Map([['a',1]]);
console.log(map.delete('a'));
console.log(map.has('a'));
true false
- Python: dict.pop(key, default) возвращает значение или default; есть dict.popitem() и del dict[key].
d = {'a':1}
print(d.pop('a'))
print(d.get('a'))
1 None
- PHP: для массивов и словарей используется unset($arr[$key]); не возвращает удалённое значение. Для получения значения сначала выполняется чтение, затем unset.
$map = ['a' => 1];
$val = $map['a'];
unset($map['a']);
var_dump($val, array_key_exists('a', $map));
int(1) bool(false)
- C#: Dictionary<K,V>.Remove(key) возвращает boolean; TryGetValue применяется, если нужно получить значение и удалить.
var dict = new Dictionary{{"a",1}};
int val;
if (dict.TryGetValue("a", out val)) {
dict.Remove("a");
}
Console.WriteLine(val);
Console.WriteLine(dict.ContainsKey("a"));
1 False
- Go: в map удаление через delete(m, key); не возвращает значение. Если требуется значение, сначала присваивается, затем delete.
m := map[string]int{"a":1}
v, ok := m["a"]
delete(m, "a")
fmt.Println(v, ok)
_, exists := m["a"]
fmt.Println(exists)
1 true false
- Kotlin: MutableMap.remove(key) возвращает предыдущее значение или null, семантика близка к Java; есть remove(key, value) для условного удаления.
- Lua: удаление реализуется через присвоение nil: t[k] = nil; не возвращает прежнего значения напрямую, поэтому его читают до удаления.
Отличия от Java: во многих языках удаление не возвращает значение напрямую (Go, PHP, Lua), поэтому чтение и удаление выполняются в два шага. Java и Kotlin поддерживают возвращаемое предыдущее значение; JavaScript Map возвращает boolean.
Типичные ошибки при использовании Map.remove
- Путаница с возвращаемым null. Если реализация допускает null-значения (например, HashMap),
remove(key)возвращает null как для отсутствия ключа, так и для существующего ключа со значением null. Решение - проверкаcontainsKey. - UnsupportedOperationException. Попытка удаления из немодифицируемой карты (Map.of, Collections.unmodifiableMap) приводит к исключению.
- NullPointerException в ConcurrentHashMap при передаче null в методы удаления. В многопоточных реализациях null не допускается.
- ConcurrentModificationException. Модификация Map во время обхода через for-each без использования итератора приводит к исключению у неконкурентных реализаций.
Примеры ошибок:
UnsupportedOperationException:
import java.util.Map;
Map m = Map.of("a",1);
m.remove("a");
Exception in thread "main" java.lang.UnsupportedOperationException at java.base/java.util.ImmutableCollections.uoe(ImmutableCollections.java:XXX) ...
ConcurrentModificationException при удалении в for-each:
import java.util.HashMap;
HashMap map = new HashMap<>();
map.put(1, "one");
map.put(2, "two");
for (Integer k : map.keySet()) {
map.remove(k);
}
System.out.println("done");
Exception in thread "main" java.util.ConcurrentModificationException at java.base/java.util.HashMap$HashIterator.nextNode(HashMap.java:XXX) ...
NullPointerException в ConcurrentHashMap:
import java.util.concurrent.ConcurrentHashMap;
ConcurrentHashMap cm = new ConcurrentHashMap<>();
cm.remove(null);
Exception in thread "main" java.lang.NullPointerException at java.base/java.util.concurrent.ConcurrentHashMap.checkKey(ConcurrentHashMap.java:XXX) ...
Изменения в поведении метода в новых версиях Java
- В Java 8 в интерфейс Map был добавлен default-метод
remove(Object key, Object value), что дало единую условную операцию удаления для всех реализаций Map без необходимости использования ConcurrentMap. - ConcurrentMap и ConcurrentHashMap имели методы условного удаления ранее, но перенос в Map как default улучшил единообразие API.
- Поведение по отношению к null и исключениям остается зависимым от конкретной реализации; в новых релизах изменения в производительности или внутренней реализации (например, оптимизации HashMap) не влияют на контракт remove.
Расширенные и редкие сценарии применения Map.remove
Сценарий 1. Безопасное удаление во время итерации с помощью итератора.
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
public class SafeRemove {
public static void main(String[] args) {
Map map = new HashMap<>();
map.put("a",1);
map.put("b",2);
Iterator> it = map.entrySet().iterator();
while (it.hasNext()) {
Map.Entry e = it.next();
if (e.getValue() % 2 == 0) {
it.remove(); // безопасное удаление
}
}
System.out.println(map);
}
}
{a=1}
Сценарий 2. Условное удаление в многопоточном окружении с ConcurrentHashMap.
import java.util.concurrent.ConcurrentHashMap;
public class ConcurrentRemove {
public static void main(String[] args) {
ConcurrentHashMap chm = new ConcurrentHashMap<>();
chm.put("k", 100);
boolean removed = chm.remove("k", 100); // атомарно
System.out.println(removed);
}
}
true
Сценарий 3. Атомарная проверка и удаление без race condition через remove(key, value) на обычной Map в Java 8+
Map map = new HashMap<>();
map.put("x", 1);
// если только одно поточное окружение, это безопасно; в многопоточном - использовать ConcurrentMap
boolean ok = map.remove("x", 1);
System.out.println(ok);
true
Сценарий 4. Удаление элементов по условию через removeIf на entrySet - удобный способ массовой фильтрации.
Map map = new HashMap<>();
map.put("a", 1);
map.put("b", 2);
map.entrySet().removeIf(e -> e.getValue() % 2 == 0);
System.out.println(map);
{a=1}
Сценарий 5. Комбинация compute и remove для условной логики, когда требуется возвращать предыдущее значение.
Map map = new HashMap<>();
map.put("c", 3);
Integer prev = map.remove("c");
if (prev != null) {
// дополнительная логика с prev
System.out.println("Удалено: " + prev);
}
System.out.println(map);
Удалено: 3
{}
Сценарий 6. Использование remove при работе с TreeMap и компаратором: удаление происходит с учётом сравнения ключей.
import java.util.Comparator;
import java.util.TreeMap;
TreeMap tm = new TreeMap<>(Comparator.reverseOrder());
tm.put("a",1);
tm.put("b",2);
System.out.println(tm.remove("a"));
System.out.println(tm);
1
{b=2}
Сценарий 7. Удаление с возвратом и логированием - шаблон для кэшей.
Map cache = new HashMap<>();
cache.put("user:1", "data1");
String removed = cache.remove("user:1");
if (removed != null) {
System.out.println("Evicted cache: " + removed);
}
Evicted cache: data1
Сценарий 8. Обход типичных ошибок: проверка на null и неподдерживаемые операции перед удалением.
Map m = Map.of("a","v");
if (!(m instanceof java.util.Collections.UnmodifiableMap)) {
// в реальном коде лучше перехватить UnsupportedOperationException
m.remove("a");
}
-- в immutable карте удаление всё равно приведёт к исключению --