• 1. 集合的迭代操作
  • 1.1. 迭代操作实例
  • 2. Iterable 接口和 Iterator 迭代器
  • 2.1. Iterator 接口中定义的对象方法
  • 2.1.1. hasNext() 方法
  • 2.1.2. next() 方法
  • 2.1.3. remove() 方法
  • 2.1.4. 关于迭代器指针的指向
  • 3. List 接口 和 ListIterator 迭代器
  • 3.1. ListIterator 接口中定义的一些方法
  • 3.1.1. hasPrevious() 方法
  • 3.1.2. previous() 方法
  • 4. Enumeration 接口
  • 4.1. Enumeration 接口中定义的方法
  • 4.1.1. hasMoreElements() 方法
  • 4.1.2. nextElement() 方法
  • 4.2. Enumeration 接口使用实例
  • 5. 深入分析 for-each 和迭代删除操作
  • 5.1. for-each 的底层操作原理
  • 5.1.1. for-each 操作数组
  • 5.1.2. for-each 操作 Iterable 实例
  • 5.2. 并发修改异常
  • 5.2.1. 并发修改异常的产生原因
  • 5.2.2. 并发修改异常的解决方案

 


1. 集合的迭代操作

  • 集合的迭代, 就是把集合中的元素一个个遍历出来.
  • 集合的迭代有四种选择:
  • for 循环/增强 for 循环
  • Iterator 迭代器
  • ListIterator 迭代器
  • Enumeration 迭代器(已淘汰)

1.1. 迭代操作实例

  • 假设如下有一个列表, 要对它进行遍历.
List list = new ArrayList();
list.add("A"); 
list.add("B"); 
list.add("C"); 
list.add("D");

//方式1: for 循环 for (int index = 0; index < list.size(); index ++){ System.out.println(list.get(index)); }

//方式1: 增强 for 循环 for (Object ele : list){ System.out.println(ele); }

//方式2: 通过 while 使用迭代器 Iterator iterator() Iterator it = list.iterator();//获取集合的迭代器 while (it.hasNext()){ System.out.println(it.next()); }

//方式2: 通过 for 使用迭代器 Iterator iterator() Iterator it = list.iterator();//获取集合的迭代器 for (Iterator it = list.iterator(); it.hasNext();){ //for 循环结束后会释放迭代器占用的资源, 效能会更好一点 System.out.println(it.next()); }

//方式3: ListIterator 正向遍历使用方式和 Iterator 一致, 不再举例 //PASS...

//方式4: 已淘汰, 在接口介绍中有实例.

## 2. Iterable 接口和 Iterator 迭代器
- `Iterator` 为 Java 中的迭代器, 是能够对集合进行迭代遍历的底层依赖.  

- 而 `Iterable` 接口里定义了返回 `Iterator` 对象的方法, 相当于对 `Iterator` 的封装,  
  因此实现了 `Iterable` 接口的类可以支持 `for-each` 循环.  

- `Collection` 接口又是 `Iterable` 的子接口, 也接收了返回 `Iterator` 对象的方法,  
  因此所有 `Collection` 集合的实现类都能用调用迭代器对象进行集合遍历.

### 2.1. Iterator 接口中定义的对象方法

#### 2.1.1. hasNext() 方法  
- 定义:  
  `boolean hasNext()`
- 作用:  
  如果仍有元素可以迭代, 则返回 `true`.

#### 2.1.2. next() 方法  
- 定义:  
  `E next()`
- 作用:  
  返回迭代的下一个元素.

#### 2.1.3. remove() 方法
- 定义:  
  `void remove()`
- 作用:  
  从迭代器指向的 `collection` 中移除迭代器返回的最后一个元素(可选操作).  
- 注意:  
  每次调用 `next()` 只能调用一次此方法. 如果进行迭代时用调用此方法之外的  
  其他方式修改了该迭代器所指向的 `collection`, 则迭代器的行为是不确定的. 

#### 2.1.4. 关于迭代器指针的指向  
  - 最开始迭代器的指针是指向第一个元素之前的位置.
  - 调用 `next()` 方法之后指针就向下移动一位.  
  - 迭代器所指位置是根据调用 `next()` 方法次数决定的.  
  - 因此 `hasNext()` 方法指向是和迭代器指向是一致的.

## 3. List 接口 和 ListIterator 迭代器
- `List` 接口提供了特殊的迭代器, 称为 `ListIterator`. 这是 `Iterator` 的子接口.

- 该迭代器除了允许 `Iterator` 接口提供的正常操作外, 还允许元素插入和替换,   
  以及双向访问, 还提供了一个方法来获取从列表中指定位置开始的列表迭代器.   

### 3.1. ListIterator 接口中定义的一些方法
- 这里只列举向前迭代的方法, 该接口比较少用到, 因此不详细描述.  

#### 3.1.1. hasPrevious() 方法  
- 定义:  
  `boolean hasPrevious()`
- 作用:  
  如果以逆向遍历列表, 列表迭代器有多个元素, 则返回 true.
- 注意:  
  反向遍历的前提是指针先指到的元素的前面有元素存在.  
  因此需要先调用 `next()` 将初始指针后移至少两位,  
  才能开始反向遍历(后移两位指向的是第二个元素).

#### 3.1.2. previous() 方法  
- 定义:  
  `E previous()`
- 作用:  
  返回列表中的前一个元素.
- 备注:  
  可以重复调用此方法来迭代列表, 或混合调用 `next()` 来前后移动.   
  (注意交替调用 `next()` 和 `previous()` 将重复返回相同的元素).

## 4. Enumeration 接口
- 这个是在集合框架存在前, JAVA1 中带有的迭代器. 目前已淘汰, 很少使用.  
  新的实现应该优先考虑使用 `Iterator` 接口而不是 `Enumeration` 接口.

- 实现 `Enumeration` 接口的对象, 它生成一系列元素, 一次生成一个.  
  连续调用 `nextElement` 方法将返回一系列的连续元素.  

### 4.1. Enumeration 接口中定义的方法

#### 4.1.1. hasMoreElements() 方法
- 定义:  
  `boolean hasMoreElements()`
- 作用:  
  测试此枚举是否包含更多的元素. 当且仅当此枚举对象至少  
  还包含一个可提供的元素时, 才返回 true; 否则返回 false.

#### 4.1.2. nextElement() 方法
- 定义:  
  `E nextElement()`  
- 作用:  
  如果此枚举对象至少还有一个可提供的元素, 则返回此枚举的下一个元素.

### 4.2. Enumeration 接口使用实例
```java
Vector v = new Vector();
v.add("A");
v.add("B");
v.add("C");
v.add("D");

//用 Enumeration 迭代器遍历集合
for (Enumeration e = v.elements(); e.hasMoreElements();){
  System.out.println(e.nextElement());
}

5. 深入分析 for-each 和迭代删除操作

5.1. for-each 的底层操作原理

  • 通常来说, 增强 for 循环能够适应很多情况下的集合遍历, 直接使用即可.
5.1.1. for-each 操作数组
  • 实际上底层还是使用的是普通 for 循环, 如下实例所示.
  • 编译前:
int[] arr = {1, 2, 3};
for(int i : arr){
System.out.println(i);
}
  • 编译后反编译:
int [] arr = {1, 2, 3};
int ai[];
int k = (ai = arr).length;
for(int j = 0; j < k; j++){
int i = ai[j];
System.out.println(i);
}
5.1.2. for-each 操作 Iterable 实例
  • 实际上底层操作的是 Iterator 迭代器对象.
  • 编译前:
List list = new ArrayList();
list.add("A");
list.add("B");
list.add("C");
for(Object ele : list){
System.out.println(ele);
}
  • 编译后反编译:
List list = new ArrayList();
list.add("A");
list.add("B");
list.add("C");
Object ele;
for(Iterator iterator = list.iterator(); iterator.hasNext();){
ele = iterator.next();
System.out.println(ele);
}

5.2. 并发修改异常

  • 当需要一边迭代集合元素, 一百年删除指定的元素时,
    只能使用迭代器, 不能使用 for-each 进行操作.
5.2.1. 并发修改异常的产生原因
  • 当使用迭代器的时候, 会在当前线程中, 再创建一个新线程.
    迭代器在这个新的线程中对原来的集合进行遍历操作.
  • 因此迭代器所在的线程是和集合所在的线程是不一样的,
    所以两个线程中的操作并不会互相同步.
  • 同时迭代器在新线程会建立一个指向原来对象的单链索引表.
    若原集合中对象数量发生变化, 该索引表的内容并不会同步改变.
  • 所以在迭代过程中在集合线程使用集合的 remove() 方法删除元素时,
    迭代器是不会知道的, 这时只有集合本身把元素删除了, 迭代器的索引表没有更新.
  • 然后在迭代器线程中, 当索引指针移到表中被删除元素本来的索引时,
    就会找不到该索引所对应的迭代对象, 因为对象已经在集合中进行删除,
    因此就会抛出并发修改异常 ConcurrentModificationException.
5.2.2. 并发修改异常的解决方案
  • 这是会产生并发修改异常的代码
  • 在用 for-each 遍历集合的过程中用集合方式删除元素
  • 最总会提示并发修改异常 ConcurrentModificationException.
public static void main(String args[]) {
List<String> famous = new ArrayList<String>();
famous.add("liudehua");
famous.add("madehua");
famous.add("liushishi");
famous.add("tangwei");
for (String s : famous) {
    if (s.equals("madehua")) {
        famous.remove(s);
    }
}
}
  • 用迭代器进行元素删除, 避免并发修改异常出现.
  • 使用迭代器 Iterator 中的 remove 方法
  • 即可以删除集合中的对象, 也能删除迭代器索引表对应索引
//不能使用foreach操作, 必须要获取迭代器
Iterator it = famous.iterator();  
String s;

//改用while循环 while(it.hasNext()) { s = it.next(); if (s.equals("madehua")) { it.remove();//这里用迭代器删除元素 } } ```