Java迭代器详解,看这一篇就够了
Java迭代器详解,看这一篇就够了
文章目录
🚩Java 迭代器详解
📚迭代器的定义
迭代器
是属于
设计模式
之一,
迭代器模式
提供了一种方法来顺序访问一个聚合对象中各个元素,而不保留该对象的内部表示。
1)
Iterator对象
称为 迭代器 ,主要用于遍历Collection集合
中的元素。2)所有实现了
Collection接口
的集合类都有一个iterator()
方法,用以返回一个实现了Iterator接口的对象
,即可以返回一个迭代器
。3)
Iterator
仅用于遍历集合,Iterator
本身并不存放对象。
📒认识Iterator
✏️类结构图
通过观察类结构图的继承关系我们发现,集合的顶层接口
Collection
继承
Iterable
接口。
✒️Iterable接口
public interface Iterable<T> {
/**
* Returns an iterator over elements of type {@code T}.
*
* @return an Iterator.
*/
Iterator<T> iterator();
}
在
Iterable接口
中有一个
Iterator方法
,它返回一个
Itertator对象
。
🖍️Iterator接口
public interface Iterator<E> {
boolean hasNext();
E next();
default void remove() {
throw new UnsupportedOperationException("remove");
}
}
📃Iterator接口的方法
返回值类型 | 方法名 | 功能 |
---|---|---|
boolean | hasNext() | 判断集合是否还有元素,如果返回 true 表示集合还有元素,返回 false 表示集合中没有元素;一般对集合的访问通过 while(hasNext()) 判断是否还需要遍历。 |
E | next() | 获取集合中遍历的当前元素 ;一般先调用 hasNext() 方法判断是否存在元素,再调用 next() 获取元素,需要进行循环交替遍历集合中的元素。 |
void | remove | 删除集合中的元素。 |
📙迭代器的使用
🏷️使用迭代器遍历集合
我们用
ArrayList集合
存放一些整型数据做示例,然后将其集合中的元素一一遍历打印输出。
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class TestDemo {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
list.add(111);
list.add(222);
list.add(333);
Iterator<Integer> iterator = list.iterator();
while(iterator.hasNext()) {
int value = iterator.next();
System.out.print(value + " ");
}
}
}
运行结果:
观察运行结果我们发现,通过
迭代器
我们将
ArrayList集合
中的元素一一打印了出来。
🔖Itertor的执行原理
在上述的示例中, 迭代器 是如何实现对集合的遍历呢?
⏳图示执行过程
⌛执行过程详解
①首先得到一个集合的迭代器
Iterator iterator = list.iterator();
②进入
while循环
,调用
hasNext()
判断是否有下一个元素,返回true,
Iterator.next()
移动一个位置,将该位置的元素
111
返回。
③再次进入
while循环
,调用
hasNext()
判断是否有下一个元素,返回true,
Iterator.next()
移动一个位置,将该位置
222
的元素返回。
④再次进入
while循环
,调用
hasNext()
判断是否有下一个元素,返回true,
Iterator.next()
移动一个位置,将该位置
333
的元素返回。
⑤再次进入
while循环
,调用
hasNext()
判断是否有下一个元素,返回false,循环结束。
在
迭代器
的遍历过程中先通过
hastNext()
方法判断是否有下一个元素,如果存在下一个元素再调用
next()
方法获取元素,在这里
next()
方法先往后移动一个元素位置,再返回该位置的元素。因此,在调用
next()
方法之前必须要调用
hastNext()
方法进行检测;如果没有调用并且没有下一个元素,直接调用
next()
方法会抛出
NoSuchElementException异常
。
🃏生成迭代器的快捷键
一开始使用
迭代器
可能会觉得麻烦,但是如果用的
Idea编译器
是有快捷键的,输入
itit
再回车就会直接生成。
📕迭代器中的remove()
⛄迭代器的remove()方法使用
在
Iterator接口
中除了
hasNext()
和
next()
方法外,还有一个
remove()
方法,即
删除集合中的元素
。
如删除上述示例集合中的元素111
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
@SuppressWarnings({"all"})
public class TestDemo {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
list.add(111);
list.add(222);
list.add(333);
System.out.println("删除前:" + list);
Iterator<Integer> iterator = list.iterator();
while (iterator.hasNext()) {
Integer value = iterator.next();
if(value == 111) iterator.remove();
}
System.out.println("删除后" + list);
}
}
运行结果:
☃️迭代器遍历中调用集合revome()方法触发异常
在Java集合中,以集合
ArrayList
为例,在使用中可能会遇到删除的需求场景,此时如果代码书写不当,极有可能会抛出
java.util.ConcurrentModificationException
异常信息。
在上述示例中用
Iterator
调用了迭代器
remove()
方法,如果在使用中不小心调用了集合中的
remove()
方法会发生什么?
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
@SuppressWarnings({"all"})
public class TestDemo {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
list.add(111);
list.add(222);
list.add(333);
System.out.println("删除前:" + list);
Iterator<Integer> iterator = list.iterator();
while (iterator.hasNext()) {
Integer value = iterator.next();
if(value == 111) list.remove(Integer.valueOf(111));
}
System.out.println("删除后" + list);
}
}
运行结果:
运行结果中抛出
java.util.ConcurrentModificationException
异常信息。这是因为触发了
集合中并发修改的异常
接下来我们通过源码对抛出异常的原因进行剖析。
public Iterator<E> iterator() {
return new Itr();
}
在
ArrayList
集合的
Iterator
方法中,是通过返回
Itr
对象来获得迭代器的。
Itr
是
ArrayList
的一个
内部类
,它实现了
Iterator
接口,
代码如下:
private class Itr implements Iterator<E> {
int cursor; // index of next element to return
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount;
Itr() {}
public boolean hasNext() {
return cursor != size;
}
@SuppressWarnings("unchecked")
public E next() {
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
}
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
Itr类属性
属性 | 含义 |
---|---|
cursor | 索引下标,表示下一个可以访问的元素的索引,默认值为 0 |
lastRet | 索引下标,表示上一个元素的索引,默认值为 -1 |
expectedModCount | 对集合修改的版本号,初始值为 ModCount |
ModCount定义在AbstractList接口中,初始值为 0 ,定义如下:
protected transient int modCount = 0;
ModCount是版本号,在对集合进行变更操作(增加、删除、修改等)的时候会对版本号进行 +1 操作。
结合上述代码进行抛出
java.util.ConcurrentModificationException
异常的解释。
①初始化ArrayList,添加三次元素,即三次调用
add()
方法,进行三次
modCount++;
此时,
m o d C o u n t
3 , s i z e
3 ; \color{red}{modCount = 3,size = 3;}
m
o
d
C
o
u
n
t
=
3
,
s
i
z
e
=
3
;
②初始化Iterator迭代器进行循环,此时,
e x p e c t e d M o d C o u n t
m o d C o u n t
3 , \color{red}{expectedModCount = modCount=3,}
e
x
p
e
c
t
e
d
M
o
d
C
o
u
n
t
=
m
o
d
C
o
u
n
t
=
3
,
c u r s o r
0 , l a s t R e t
− 1 \color{red}{cursor=0,lastRet = -1}
c
u
r
s
o
r
=
0
,
l
a
s
t
R
e
t
=
−
1
③进行hasNext判断,
cursor != size;
成立,进入循环
④调用
next()
方法,首先进行
checkForComodification()
校验,
m o d C o u n t
= e x p e c t e d M o d C o u n t \color{red}{modCount == expectedModCount}
m
o
d
C
o
u
n
t
=
=
e
x
p
e
c
t
e
d
M
o
d
C
o
u
n
t ,校验通过,返回值,此时
l a s t R e t
0 ; c u r s o r
1 \color{red}{lastRet = 0;cursor = 1}
l
a
s
t
R
e
t
=
0
;
c
u
r
s
o
r
=
1
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
⑤调用集合
remove()
方法,
modCount++;
,此时
m o d C o u n t
4 ; s i z e
2 \color{red}{modCount = 4;size = 2}
m
o
d
C
o
u
n
t
=
4
;
s
i
z
e
=
2
⑥再次调用hasNext()方法判断,
cursor != size;
成立,进入循环
⑦调用
next()
方法进行校验,
m o d C o u n t !
e x p e c t e d M o d C o u n t \color{red}{modCount != expectedModCount}
m
o
d
C
o
u
n
t
!
=
e
x
p
e
c
t
e
d
M
o
d
C
o
u
n
t
,校验未通过,抛出
java.util.ConcurrentModificationException
异常
总结:
①在使用 迭代器 的
remove()
操作时,会将更新后的modCount
给expectedModCount
,两者会得到同步,但是在调用集合的remove()
方法后,两个不会进行同步,进而导致在checkForComodification()
校验时不通过,抛出java.util.ConcurrentModificationException
异常。②所以,在单线程下使用迭代器是没有问题的,但是在多线程下同时操作集合就不允许了,可以通过 fail-fast 快速失败机制,快速判断是否存在同时操作问题。因此, 集合在多线程下使用是不安全的 。
📗增强for循环
📫认识增强for循环
增强for循环
可以代替 Iterator迭代器 ,可以把它看做简化版的Iterator,和迭代器本质一样,其实它的底层实现就是Iterator迭代器,只能用于 遍历集合或数组 。
📪基本语法
📬增强for循环的使用
import java.util.ArrayList;
import java.util.List;
public class TestDemo03 {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
list.add(111);
list.add(222);
list.add(333);
System.out.println("====增强for循环遍历集合====");
for(Integer i : list) {
System.out.print(i + " ");
}
System.out.println();
System.out.println("====增强for循环遍历数组====");
int[] arr = {1,2,3,4,5,6};
for (int i : arr) {
System.out.print(i + " ");
}
}
}
运行结果:
📭增强for循环的快捷键
与迭代器一样,增强for循环也有快捷键,输入
I
回车即可快速生成。