Итератор и ListIterator
все, что реализованоCollection
Класс коллекции интерфейса имеетiterator
метод, который возвращает реализацию, реализующуюIterator
Объект интерфейса для обхода коллекции.Iterator
Интерфейс имеет три основных метода, а именноhasNext
,next
,remove
метод.
ListIterator
унаследовано отIterator
, специально для реализацииList
объекты интерфейса, кромеIterator
Кроме методов интерфейса есть еще несколько методов.
на основе последовательного храненияIterator
Данные могут быть доступны непосредственно по местоположению. на основе коллекции цепных хранилищIterator
, как правило, необходимо сохранить текущую пройденную позицию, а затем переместить указатель вперед или назад в соответствии с текущей позицией.
Iterator
а такжеListIterator
Разница:
-
Iterator
можно использовать для обходаSet
,List
;ListIterator
доступен только для обходаList
. -
Iterator
может двигаться только назад;ListIterator
Его можно пройти вперед или назад. -
ListIterator
ДостигнутоIterator
интерфейс и добавляетadd
,set
,hasPrevious
,previous
,previousIndex
,nextIndex
метод.
отказоустойчивый
безотказный механизм(fail—fast
) при использовании итератора для обхода объекта коллекции, если коллекция модифицируется (добавляется, удаляется, изменяется) в процессе обхода, она выдаетConcurrentModificationException
аномальный.
Например, следующий код выдастConcurrentModificationException
:
List<String> stringList = new ArrayList<>();
stringList.add("abc");
stringList.add("def");
Iterator<String> iterator = stringList.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
stringList.add("ghi");
}
ПроверятьArrayList
Исходный код, вы можете знать, почему возникает исключение. Причина в том,ArrayList
итератор внутреннего класса классаItr
существует одинexpectedModCount
Переменная. существуетAbstracList
абстрактный класс имеетmodCount
Переменная, коллекция изменится, если содержимое изменится во время ее обхода.modCount
значение . всякий раз, когда используется итераторnext()
Прежде чем перейти к следующему элементу, он проверитmodCount
Является ли переменная равнойexpectedmodCount
, если они равны, продолжить обход, иначе будет выброшено исключение.
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
Примечание. Обнаружено условие создания этого исключения.modCount != expectedmodCount
. Если коллекция изменитсяmodCount
Значение просто установлено наexpectedmodCount
, то исключение не будет выброшено. Таким образом, параллельные операции не могут выполняться в зависимости от того, выброшено ли это исключение.Это исключение рекомендуется только для обнаружения одновременных изменений.bug
.
существуетjava.util
Все классы коллекций в пакете используют отказоустойчивый механизм, поэтому при многопоточности параллельные модификации не могут происходить, то есть они не могут быть изменены в процессе итерации.
отказоустойчивый
с помощью отказоустойчивого механизма (fail—safe
) класса коллекции, при обходе коллекции он не обращается напрямую к исходной коллекции, а сначала копирует содержимое исходной коллекции, а затем обходит скопированную коллекцию. Поскольку скопированная коллекция проходится, изменение исходной коллекции в процессе обхода не будет обнаружено итератором, поэтому он не выдастConcurrentModificationException
аномальный.
Хотя механизм безопасного отказа, основанный на копировании содержимого, позволяет избежатьConcurrentModificationException
, но итератор не имеет доступа к измененному содержимому, но по-прежнему является копией коллекции, полученной в момент начала обхода.
существуетjava.util.concurrent
Все коллекции в пакете используют безопасный механизм отказа, поэтому одновременное использование и операции модификации могут выполняться в многопоточных сценариях.
Как удалить элементы при циклическом просмотре коллекции
При обходе коллекции правильные методы удаления следующие:
нормальный для цикла
При использовании обычного цикла for, если вы перемещаетесь спереди назад:
ArrayList<String> stringList = new ArrayList<>();
stringList.add("abc");
stringList.add("def");
stringList.add("def");
stringList.add("ghi");
for (int i = 0;i < stringList.size(); i++) {
String str = stringList.get(i);
if ("def".equals(str)) {
stringList.remove(str);
}
}
Результат печати:
abc def ghi
Как видите, второй здесь пропущен."def"
. Причина в том, что в началеList
изsize
за4
, спереди назад, петля к указателю#1
, найдено удовлетворяющее условиям, поэтому удалено#1
Элементы. В настоящее времяList
изsize
стать3
,показатель#1
указал на перед#2
элемент (то есть#2
элементы перемещены#1
,#3
переехал в#2
).
И следующий цикл начнется с индекса#2
Старт, просмотр перед удалением#3
элементы , поэтому перед#2
элементы (перемещены влево в#1
) пропускается.
И если вы перемещаетесь сзади вперед, вы можете избежать воздействия движения элемента.
ArrayList<String> stringList = new ArrayList<>();
stringList.add("abc");
stringList.add("def");
stringList.add("def");
stringList.add("ghi");
for (int i = stringList.size() - 1;i >= 0; i--) {
String str = stringList.get(i);
if ("abc".equals(str)) {
stringList.remove(str);
}
}
// abc ghi
foreach, чтобы выйти из цикла после удаления
в настоящее время используетforeach
Когда итератор проходит через коллекцию, он используется после удаления элемента.break
вне цикла, он не сработаетfail-fast
.
for (String str : stringList) {
if ("abc".equals(str)) {
stringList.remove(str);
break;
}
}
использовать итератор
Используйте итератор, который идет с нимremove
Метод удаляет элемент и не генерирует исключение.
Iterator<String> iterator = stringList.iterator();
while (iterator.hasNext()) {
String str = iterator.next();
if ("abc".equals(str)) {
iterator.remove(); // 这里是 iterator,而不是 stringList
}
}
Enumeration
Enumeration
даJDK1.0
Представленный интерфейс предоставляет интерфейс для обхода коллекций, а коллекции, которые его используют, включаютVector
,HashTable
Ждать.Enumeration
Итераторы не поддерживаютсяfail-fast
механизм.
У него всего два метода интерфейса:hasMoreElements
,nextElement
Он используется для определения наличия элемента и получения элемента, но не может изменять данные.
Но следует отметить, чтоEnumeration
Итераторы могут проходить толькоVector
,HashTable
Это древняя коллекция, поэтому обычно ее не используют.
Несколько способов обхода Map в Java
Метод, используемый в записях цикла for-each для итерации
Это наиболее распространенный и в большинстве случаев предпочтительный способ обхода, и он используется, когда нужны и ключи, и значения.
Map<Integer, Integer> map = new HashMap<>();
for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue());
}
Примечание: при повторении пустогоmap
объект,for-each
петля будет бросатьNullPointerException
, поэтому перед обходом следует проверять наличие нулевых ссылок.
Метод 2 Перебор ключей или значений в цикле for-each
Если вам нужно толькоmap
Ключ или значение вkeySet
илиvalues
для реализации обхода вместо использованияentrySet
.
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
//遍历 map 中的键
for (Integer key : map.keySet()) {
System.out.println("Key = " + key);
}
//遍历 map 中的值
for (Integer value : map.values()) {
System.out.println("Value = " + value);
}
Этот метод болееentrySet
Traversal немного лучше по производительности, а код чище.
Способ 3. Используйте итератор для обхода
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
Iterator<Map.Entry<Integer, Integer>> entries = map.entrySet().iterator();
while (entries.hasNext()) {
Map.Entry<Integer, Integer> entry = entries.next();
System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue());
}
Этот метод выглядит избыточным, но имеет свои преимущества, его можно вызывать при обходеiterator.remove()
удалитьentries
, два других метода не могут.
С точки зрения производительности этот метод аналогиченfor-each
Производительность обхода (т.е. способ второй).
Суммировать
- Если только ключ(
keys
) или значение (values
), затем используйте метод 2; - Если вам нужно удалить во время обхода
entries
, затем используйте третий метод; - Если требуются и ключ, и значение, используйте метод 1.