java迭代器及foreach详解

一.迭代器 我们知道,在集合框架中,Collection是根接口,而Collection继承了Iterable接口,所以所有实现collection接口的类都可以使用迭代器.
java迭代器及foreach详解
文章图片

在集合框架中,ArrayList,LinkedList,HashSet,TreeSet都可以使用迭代器.我们看看Iterable接口定义了哪些方法.

public interface Iterable { //返回实现java.util.Iterator迭代器对象 public abstract Iterator iterator(); }

iterator方法要求返回一个Iterator类型的对象,我们再来看看这个Iterator是什么.
跳转到Iterator中,我们发现它也是一个接口
public interface Iterator { //判断是否有后继元素,若有则返回true boolean hasNext(); //返回后继元素,如果没有后继元素,就抛出异常 E next(); //删除迭代器对象表示的集合当前元素 void remove(); }

Iterator 是为了实现对java容器(collection)进行遍历功能的1个接口.
我们定义iterator 是实现了Iterator 接口的1个对象.
首先iterator 实现了Iterator接口后, 相当于把1个Collection容器的所有对象, 做成1个线性表(List) . 而iterator本身于1个指针.
这个指针一开始是位于 第1个元素之前的.
boolean hasNext();
判断iterator 内是否存在下1个元素, 如果存在返回true, 否则返回false.
注意, 这时上面的那个指针位置不变.
Object next();
返回iterator 内下1个元素, 同时上面的指针向后移动一位.
如果不断地循环执行next()方法, 就可以遍历容器内所有的元素了.
void remove();
删除iterator 内指针的前1个元素, 前提是至少执行过1次next();
这个方法不建议使用, 建议使用容器本身的remove方法.
我们可以使用迭代器遍历一个集合,如下:
public class Iterator1{ public static void main(String[] args) { List> list=new LinkedList>(); list.add("Jack"); list.add("Bill"); Iterator it1=list.iterator(); System.out.println("迭代器遍历(while)"); while(it1.hasNext()) { System.out.println(it1.next()); } System.out.println("迭代器遍历(for)"); for(Iterator it2=list.iterator(); it2.hasNext(); ) { System.out.println(it2.next()); } } } //程序输出结果为 //迭代器遍历(while) //Jack //Bill //迭代器遍历(for) //Jack //Bill

list的iterator()方法会new一个迭代器对象返回.上面的while和for其实都是一样的.next方法一个个向下遍历,直到hasNext()返回false时,循环结束.
二.foreach循环 一个类只要实现了Iterable接口,就可以使用迭代器遍历,也可以使用foreach语句.java迭代器及foreach详解
文章图片

声明的循环变量的类型必须和数组的类型相同
import java.util.*; public class Iterator1{ public static void main(String[] args) { List> list=new LinkedList>(); list.add("Jack"); list.add("Bill"); for(String elem:list) { System.out.println(elem); } } } //程序输出结果为 //Jack //Bill

如果把elem改成char类型,编译器会报错:Type mismatch: cannot convert from element type String to char
foreach虽然能遍历数组或者集合,但是只能用来遍历,无法在遍历过程中对数组或者集合进行修改,而for循环可以在遍历的过程中对原数组或者集合进行修改
import java.util.*; public class Iterator1{ public static void main(String[] args) { List> list=new LinkedList>(); list.add("Jack"); list.add("Bill"); for(String elem:list) { elem="233"; } System.out.println(list.toString()); } } //程序输出结果为 //[Jack, Bill]

这里我们把每个循环变量赋值为233,但是结果却没有任何变化.
下面我们将深入讲解一下,为什么赋值没有用
foreach语句等同于使用迭代器进行遍历,如下图
java迭代器及foreach详解
文章图片

两种形式可以相互转换
我们在foreach语句中,定义的循环变量是一个临时变量.
在上面的例子中
for(String elem:list) { elem="233"; }

我们一个个带入上面的图中,type=String,var=elem,coll=list,循环体为elem=”233”;
可以把foreach转换成迭代器形式.
for(Iterator> iter=list.iterator(); iter.hasNext(); ){ String elem=iter.next(); 循环体 }

循环体为elem=”233”;
for(Iterator> iter=list.iterator(); iter.hasNext(); ){ String elem=iter.next(); elem="233"; }

由上面的可以知道,233赋值的是elem临时变量,并没有改变集合中的元素,所以foreach只能用来遍历,无法改变数组或者集合中的元素.
三.自己实现Iterable接口
public class SeqList{ 、 //顺序表 protected Object[] element; protected int n; public SeqList(int length) { //初始化数组 this.element=new Object[length]; this.n=0; } public SeqList() { this(64); //如果什么参数都不加,那么默认给你申请长度为64的数组 } public SeqList(T[] values) { this(values.length); //初始化长度为values.length的数组 for(int i=0; i=0 && i0 && i>=0 &&i

这里有一个顺序表类,n代表数组的长度,get取到第i个元素.remove删除第i个元素,我们想要让SeqList类可以使用foreach遍历.
想要让自己写的类,可以使用foreach遍历,必须实现iterable接口,我们在类名后面添加implements关键字:
public class SeqList implements java.lang.Iterable

还要实现接口中的方法(iterator):
public java.util.Iterator iterator(){ return new SeqIterator(); }

iterator方法返回一个迭代器对象,这里return的类的名字可以随便取,我们取名叫SeqIterator
返回的SeqIterator类的对象必须实现Iterator接口(不是Iterable)
private class SeqIterator implements java.util.Iterator{ int index=-1,succ=0; //index代表当前元素,succ代表后继元素 public boolean hasNext() { `//判断是否有后继元素 return this.succ.this.n; } public T next() { //返回后继元素 T value=https://www.it610.com/article/SeqList.this.get(this.succ); if(value!=null) { this.index=this.succ++; return value; } throw new java.util.NoSuchElementException(); } public void remove() { //删除第 index个元素 if(this.index>=0&&this.index.this.n) { SeqList.this.remove(this.index); if(this.succ>0) { this.succ--; } this.index=-1; //设置不能连续删除 }else { throw new java.util.NoSuchElementException(); } } }

SeqList的全部代码为:
public class SeqList implements java.lang.Iterable{ public java.util.Iterator iterator(){ return new SeqIterator(); } private class SeqIterator implements java.util.Iterator{ int index=-1,succ=0; public boolean hasNext() { return this.succ.this.n; } public T next() { T value=https://www.it610.com/article/SeqList.this.get(this.succ); if(value!=null) { this.index=this.succ++; return value; } throw new java.util.NoSuchElementException(); } public void remove() { if(this.index>=0&&this.index.this.n) { SeqList.this.remove(this.index); if(this.succ>0) { this.succ--; } this.index=-1; }else { throw new java.util.NoSuchElementException(); } } } //顺序表 protected Object[] element; protected int n; public SeqList(int length) { //初始化数组 this.element=new Object[length]; this.n=0; } public SeqList() { this(64); //如果什么参数都不加,那么默认给你申请长度为64的数组 } public SeqList(T[] values) { this(values.length); //初始化长度为values.length的数组 for(int i=0; i=0 && i0 && i>=0 &&i

其中,SeqIterator内部类实现Iterator迭代器接口,为迭代器对象提供hasNext(),next()和remove()方法。SeqIterator类声明succ变量记住迭代过程中的后继元素,每次调用next()方法,获得第succ个元素,再succ++,直到最后一个元素
remove()方法在每次遍历过程中,只能使用一次.这也是为什么我们在remove方法中要写this.index=-1; 。
remove方法不建议使用, 建议使用容器本身的remove方法
测试类:
public class 数组顺序表测试 { public static void main(String[] args) { // TODO Auto-generated method stub String values[]= {"a","b","c","d","e"}; SeqList> lista; lista=new SeqList>(values); for(String elem: lista) { System.out.println(elem); } } } //程序输出结果: //a //b //c //d //e

foreach的运行其实就可以看成使用迭代器的for循环。
java迭代器及foreach详解
文章图片

我们在自己类中实现迭代器接口时,如果hasNext(),next()函数写错了, 那么当我们使用foreach循环时,程序其实就是按照右边的循环执行的.
四.foreach+Lambda表达式 炫技技巧
不多废话,直接看例子应该就能明白
ArrayList_lambda:
List> items = new ArrayList<>(); items.add("A"); items.add("B"); items.add("C"); items.add("D"); items.add("E"); //lambda //Output : A,B,C,D,E items.forEach(item->System.out.println(item)); //Output : C items.forEach(item->{ if("C".equals(item)){ System.out.println(item); } });

hashmap_lambda:
Map, Integer> items = new HashMap<>(); items.put("A", 10); items.put("B", 20); items.put("C", 30); items.put("D", 40); items.put("E", 50); items.put("F", 60); items.forEach((k,v)->System.out.println("Item : " + k + " Count : " + v)); items.forEach((k,v)->{ System.out.println("Item : " + k + " Count : " + v); if("E".equals(k)){ System.out.println("Hello E"); } });

可以看出在foreach括号中左边是参数列表,中间是->,右边就是想要实现的函数体
五.总结 总结一下,我们拿到一个集合,可以有四种方法遍历它
1.普通的for循环
public class Iterator1{ public static void main(String[] args) { List> list=new LinkedList>(); list.add("Jack"); list.add("Bill"); for(int i=0; i

2.迭代器实现
import java.util.*; public class Iterator1{ public static void main(String[] args) { List> list=new LinkedList>(); list.add("Jack"); list.add("Bill"); Iterator it1=list.iterator(); System.out.println("迭代器实现(while)"); while(it1.hasNext()) { System.out.print(it1.next()+""); } System.out.println(); System.out.println("迭代器实现(for)"); for(Iterator it2=list.iterator(); it2.hasNext(); ) { System.out.print(it2.next()+""); } } } //程序输出结果为 //迭代器实现(while) //JackBill //迭代器实现(for) //JackBill

3.foreach循环
import java.util.*; public class Iterator1{ public static void main(String[] args) { List> list=new LinkedList>(); list.add("Jack"); list.add("Bill"); for(String elem:list) { System.out.printf(elem+""); } } } //程序输出结果为 //JackBill

4.foreach+Lambda
import java.util.*; public class Iterator1{ public static void main(String[] args) { List> list=new LinkedList>(); list.add("Jack"); list.add("Bill"); list.forEach(elem->{System.out.println(elem); }); } } //程序输出结果为 //Jack //Bill

【java迭代器及foreach详解】==============================================================================
参考博客:
java-foreach实现原理
java 自定义类如何实现foreach循环
How does the Java ‘for each’ loop work?
Java Iterator 接口简介和简单用法

    推荐阅读