Remove: примеры (JAVA)
remove(int index): EОписание и сигнатуры методов remove
В Java слово remove встречается в нескольких методах стандартной библиотеки. Основные варианты:
- boolean Collection.remove(Object o) - удаляет один экземпляр элемента, равный o по equals. Возвращает
true, если коллекция изменилась. - E List.remove(int index) - удаляет элемент по индексу и возвращает удалённое значение. При неверном индексе выбрасывает
IndexOutOfBoundsException. - void Iterator.remove() - удаляет последний возвращённый итератором элемент. Должен вызываться только после next() и не более одного раза на итерацию; в противном случае возникает
IllegalStateException. Может выбрасыватьUnsupportedOperationException, если операция не поддерживается реализацией. - V Map.remove(Object key) - удаляет пару с ключом key и возвращает прежнее значение или
null, если пары не было или прежнее значение было null. Чтобы отличить случаи, использоватьcontainsKey. - boolean Map.remove(Object key, Object value) - удаляет пару только если ключ сопоставлен с указанным значением; возвращает
true, если удаление произошло. Полезно для атомарной проверки и удаления. - boolean Collection.removeIf(Predicate<? super E> filter) - добавлён в Java 8; удаляет все элементы, соответствующие предикату; возвращает
true, если коллекция изменилась.
Поведение сравнения элементов зависит от реализации коллекции: обычно используется equals для объектов. Некоторые реализации (например, специализированные структуры) могут иметь собственные правила. При работе с неизменяемыми или фиксированными коллекциями вызов методов удаления приводит к UnsupportedOperationException. При одновременной модификации из другого потока без синхронизации возможны ConcurrentModificationException или неконсистентные результаты.
Короткие примеры использования
Пример 1. Collection.remove(Object) - удаление по значению
List list = new ArrayList<>(Arrays.asList("a", "b", "c"));
boolean removed = list.remove("b");
System.out.println(removed);
System.out.println(list);
true [a, c]
Пример 2. List.remove(int) - удаление по индексу
List list2 = new ArrayList<>(Arrays.asList("a", "b", "c"));
String r = list2.remove(1);
System.out.println(r);
System.out.println(list2);
b [a, c]
Пример 3. Iterator.remove() - корректное удаление во время итерации
List list3 = new ArrayList<>(Arrays.asList("x", "y", "z"));
Iterator it = list3.iterator();
while (it.hasNext()) {
String s = it.next();
if (s.equals("y")) {
it.remove();
}
}
System.out.println(list3);
[x, z]
Пример 4. Map.remove(key) и Map.remove(key, value)
Map map = new HashMap<>();
map.put("a", 1);
map.put("b", null);
Integer prev = map.remove("a");
System.out.println(prev);
Integer prevNull = map.remove("b");
System.out.println(prevNull);
boolean removedPair = map.remove("c", 3);
System.out.println(removedPair);
1 null false
Пример 5. Collection.removeIf(Predicate) - удаление по условию
List nums = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5));
boolean any = nums.removeIf(n -> n % 2 == 0);
System.out.println(any);
System.out.println(nums);
true [1, 3, 5]
Похожие методы в Java и их особенности
- clear() - удаляет все элементы; предпочтительнее, когда нужно полностью опустошить коллекцию вместо поэлементного удаления.
- removeAll(Collection<?> c) - удаляет все элементы, входящие в указанную коллекцию; удобна для массового удаления по множеству значений.
- retainAll(Collection<?> c) - оставляет только элементы, входящие в c; эффективна при фильтрации наборов.
- Stream.filter(...).collect(...) (Java 8+) - создаёт новую коллекцию с отфильтрованными элементами; полезно, если требуется оставлять исходную коллекцию нетронутой и работать в функциональном стиле.
Выбор зависит от требуемой семантики: для атомарного удаления пары в потокобезопасной ситуации подходит Map.remove(key, value), для массовой фильтрации без модификации исходной структуры - Stream, для очистки - clear().
Аналогичные операции в других языках
- JavaScript
Array.prototype.splice для удаления по индексу, Array.prototype.filter для фильтрации, Map.prototype.delete для Map.
let arr = ['a','b','c']; let removed = arr.splice(1,1); // удаляет 1 элемент по индексу 1 console.log(removed); console.log(arr);['b'] ['a','c']
- Python
list.remove(value) удаляет первое совпадение, list.pop(index) удаляет по индексу; del для удаления срезов; set.discard/ remove для множеств.
lst = ['a','b','c'] lst.remove('b') print(lst)['a', 'c']
- PHP
unset($array[$index]) удаляет элемент по ключу; array_splice для сдвига элементов; для ассоциативных массивов unset удаляет пару.
$a = ['a','b','c']; array_splice($a,1,1); print_r($a);Array ( [0] => a [1] => c ) - SQL
DELETE FROM table WHERE condition - удаляет строки в базе, эквивалент масcовому remove для набора записей.
- C#
List
.Remove(value), RemoveAt(index), Dictionary<K,V>.Remove(key). Синтаксис и семантика близки к Java. var list = new List<string> {"a","b","c"}; list.Remove("b"); Console.WriteLine(string.Join(",", list));a,c
- Go
Нет встроенной функции remove для срезов: используется комбинация append и срезов; для map используется
delete(map, key).s := []int{1,2,3} s = append(s[:1], s[2:]...) fmt.Println(s)[1 3]
- Kotlin
MutableList.remove(value) и removeAt(index), MutableMap.remove(key) - API и поведение аналогичны Java, с дополнениями расширений и удобных функций.
- Lua
table.remove(t, index) удаляет элемент по индексу и сдвигает последующие элементы.
Отличия от Java: в некоторых языках удаление по индексу или по значению ведёт себя иначе при отсутствии элемента (вызов ошибки или молчаливый возврат), и есть различные способы атомарного удаления в конкурентной среде.
Типичные ошибки при использовании remove
- Удаление из коллекции во время итерации с использованием for-each приводит к ConcurrentModificationException.
- Вызов Iterator.remove() до first next() или при повторном remove() вызывает IllegalStateException.
- Map.remove(key) возвращает null как для отсутствия пары, так и для наличия пары со значением null - возможная двусмысленность.
- Попытка удалить из неизменяемой или фиксированной коллекции вызывает UnsupportedOperationException (например, список из Arrays.asList).
- Collection.removeIf(null) или removeIf с null-предикатом приведёт к NullPointerException.
Примеры ошибок:
ConcurrentModificationException при удалении в for-each
List<String> list = new ArrayList<>(Arrays.asList("a","b","c"));
for (String s : list) {
if (s.equals("b")) {
list.remove(s); // вызовет ConcurrentModificationException
}
}
Exception in thread "main" java.util.ConcurrentModificationException
at java.base/java.util.ArrayList$Itr.checkForComodification(ArrayList.java:1043)
...
UnsupportedOperationException для Arrays.asList
List<String> fixed = Arrays.asList("a","b");
fixed.remove("a"); // UnsupportedOperationException
Exception in thread "main" java.lang.UnsupportedOperationException
at java.base/java.util.AbstractList.remove(AbstractList.java:...)
...
IllegalStateException при неправильном использовании Iterator.remove()
Iterator<String> it = Arrays.asList("a","b").iterator();
it.remove(); // IllegalStateException, next() не вызывался
Exception in thread "main" java.lang.IllegalStateException
at java.base/java.util.ArrayList$Itr.remove(ArrayList.java:...)
...Изменения и добавления в последних версиях
- removeIf появился в Java 8 как часть расширений коллекций для работы с лямбдами и предикатами; до этого массовую фильтрацию приходилось реализовывать вручную через итераторы или stream + collect.
- Map.remove(key, value) как атомарная проверка и удаление стала широко использоваться с развитием конкурентных коллекций и утилит в Java 8 и далее.
- В целом сигнатуры базовых методов remove остались стабильными, но появились удобные утилиты и улучшения для параллельной и функциональной работы с коллекциями.
Расширенные и редкие сценарии использования
1) Удаление дублей с сохранением порядка
List<String> data = new ArrayList<>(Arrays.asList("a","b","a","c","b"));
Set<String> seen = new HashSet<>();
data.removeIf(s -> !seen.add(s));
System.out.println(data);
[a, b, c]
2) Атомарное удаление в ConcurrentHashMap по паре ключ-значение
ConcurrentHashMap<String,Integer> cmap = new ConcurrentHashMap<>();
cmap.put("k", 1);
boolean removed = cmap.remove("k", 2); // вернёт false, ничего не удалит
System.out.println(removed);
removed = cmap.remove("k", 1); // вернёт true
System.out.println(removed);
false true
3) Удаление элементов Map по условию на entrySet (без ConcurrentModification при использовании итератора)
Map<String,Integer> map = new HashMap<>();
map.put("a", 1);
map.put("b", 10);
map.put("c", 5);
map.entrySet().removeIf(e -> e.getValue() < 6);
System.out.println(map);
{b=10}
4) Удаление при итерации с ListIterator: возможность безопасно менять последовательность
List<String> lst = new ArrayList<>(Arrays.asList("1","2","3"));
ListIterator<String> li = lst.listIterator();
while (li.hasNext()) {
String v = li.next();
if (v.equals("2")) {
li.set("2-updated"); // обновление текущего элемента
li.add("2.5"); // вставка после текущей позиции
// можно вызвать li.remove() только если предыдущий метод next/previous был вызван
}
}
System.out.println(lst);
[1, 2-updated, 2.5, 3]
5) CopyOnWriteArrayList - итераторы не бросают ConcurrentModification, но не отражают последующие модификации
CopyOnWriteArrayList<String> cow = new CopyOnWriteArrayList<>(Arrays.asList("a","b","c"));
for (String s : cow) {
if (s.equals("b")) {
cow.remove(s); // безопасно, не бросит ConcurrentModification, но итератор по старому снимку
}
}
System.out.println(cow);
[a, c]
6) Удаление сложных условий, комбинирование с Stream
List<User> users = ...; // большая коллекция
// Оставить пользователей старше 18 и удалить остальных
users.removeIf(u -> u.getAge() <= 18);
// Альтернатива: собрать отфильтрованный список
List<User> adults = users.stream().filter(u -> u.getAge() > 18).collect(Collectors.toList());
(в зависимости от входных данных изменяется коллекция или формируется новый список)
7) Удаление элементов файловой системы - пример использования API вне коллекций
Path p = Paths.get("temp.txt");
try {
Files.deleteIfExists(p);
System.out.println("deleted");
} catch (IOException e) {
e.printStackTrace();
}
deleted (если файл существовал)
В каждом расширенном сценарии стоит учитывать семантику возвращаемых значений и особенности реализации коллекции: атомарность в конкурентной среде, поддержка операции удаления и поведение итераторов.