荐 java迭代器及foreach详解
一.迭代器
我们知道,在集合框架中,Collection是根接口,而Collection继承了Iterable接口,所以所有实现collection接口的类都可以使用迭代器.
在集合框架中,ArrayList,LinkedList,HashSet,TreeSet都可以使用迭代器.我们看看Iterable接口定义了哪些方法.
public interface Iterable<T> {
//返回实现java.util.Iterator迭代器对象
public abstract Iterator<T> iterator();
}
iterator方法要求返回一个Iterator类型的对象,我们再来看看这个Iterator是什么.
跳转到Iterator中,我们发现它也是一个接口
public interface Iterator<E> {
//判断是否有后继元素,若有则返回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<String> list=new LinkedList<String>();
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语句.
声明的循环变量的类型必须和数组的类型相同
import java.util.*;
public class Iterator1{
public static void main(String[] args) {
List<String> list=new LinkedList<String>();
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<String> list=new LinkedList<String>();
list.add("Jack");
list.add("Bill");
for(String elem:list) {
elem="233";
}
System.out.println(list.toString());
}
}
//程序输出结果为
// [Jack, Bill]
这里我们把每个循环变量赋值为233,但是结果却没有任何变化.
下面我们将深入讲解一下,为什么赋值没有用
foreach语句等同于使用迭代器进行遍历,如下图
两种形式可以相互转换
我们在foreach语句中,定义的循环变量是一个临时变量.
在上面的例子中
for(String elem:list) {
elem="233";
}
我们一个个带入上面的图中,type=String,var=elem,coll=list,循环体为elem=”233”;
可以把foreach转换成迭代器形式.
for(Iterator<String> iter=list.iterator();iter.hasNext();){
String elem=iter.next();
循环体
}
循环体为elem=”233”;
for(Iterator<String> iter=list.iterator();iter.hasNext();){
String elem=iter.next();
elem="233";
}
由上面的可以知道,233赋值的是elem临时变量,并没有改变集合中的元素,所以foreach只能用来遍历,无法改变数组或者集合中的元素.
三.自己实现Iterable接口
public class SeqList<T>{
、 //顺序表
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<values.length;i++) {
this.element[i]=values[i];
}
this.n=element.length;
}
public T get(int i) {
//返回第i个元素
if (i>=0 && i<this.n) {
return (T)this.element[i];
}
return null;
}
public T remove(int i) {
if(this.n>0 && i>=0 &&i <this.n) {
T old =(T)this.element[i];
//old保存要删除的元素
for(int j=i;j<this.n-1;j++) {
//后面的元素依次前移
this.element[j]=this.element[j+1];
}
//把最后的元素置零
this.element[this.n-1]=null;
this.n--;
return old;
}
return null;
}
}
这里有一个顺序表类,n代表数组的长度,get取到第i个元素.remove删除第i个元素,我们想要让SeqList类可以使用foreach遍历.
想要让自己写的类,可以使用foreach遍历,必须实现iterable接口,我们在类名后面添加implements关键字:
public class SeqList<T> implements java.lang.Iterable<T>
还要实现接口中的方法(iterator):
public java.util.Iterator<T> iterator(){
return new SeqIterator();
}
iterator方法返回一个迭代器对象,这里return的类的名字可以随便取,我们取名叫SeqIterator
返回的SeqIterator类的对象必须实现Iterator接口(不是Iterable)
private class SeqIterator implements java.util.Iterator<T>{
int index=-1,succ=0;
//index代表当前元素,succ代表后继元素
public boolean hasNext() {
` //判断是否有后继元素
return this.succ<SeqList.this.n;
}
public T next() {
//返回后继元素
T value=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<SeqList.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<T> implements java.lang.Iterable<T>{
public java.util.Iterator<T> iterator(){
return new SeqIterator();
}
private class SeqIterator implements java.util.Iterator<T>{
int index=-1,succ=0;
public boolean hasNext() {
return this.succ<SeqList.this.n;
}
public T next() {
T value=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<SeqList.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<values.length;i++) {
this.element[i]=values[i];
}
this.n=element.length;
}
public T get(int i) {
//返回第i个元素
if (i>=0 && i<this.n) {
return (T)this.element[i];
}
return null;
}
public T remove(int i) {
if(this.n>0 && i>=0 &&i <this.n) {
T old =(T)this.element[i];
//old保存要删除的元素
for(int j=i;j<this.n-1;j++) {
//后面的元素依次前移
this.element[j]=this.element[j+1];
}
//把最后的元素置零
this.element[this.n-1]=null;
this.n--;
return old;
}
return null;
}
}
其中,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<String> lista;
lista=new SeqList<String>(values);
for(String elem: lista) {
System.out.println(elem);
}
}
}
//程序输出结果:
// a
// b
// c
// d
// e
foreach的运行其实就可以看成使用迭代器的for循环。
我们在自己类中实现迭代器接口时,如果hasNext(),next()函数写错了, 那么当我们使用foreach循环时,程序其实就是按照右边的循环执行的.
四.foreach+Lambda表达式
炫技技巧
不多废话,直接看例子应该就能明白
ArrayList_lambda:
List<String> 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<String, 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<String> list=new LinkedList<String>();
list.add("Jack");
list.add("Bill");
for(int i=0;i<list.size();i++) {
System.out.print(list.get(i)+" ");
}
}
}
//程序输出结果为
// Jack Bill
2.迭代器实现
import java.util.*;
public class Iterator1{
public static void main(String[] args) {
List<String> list=new LinkedList<String>();
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)
// Jack Bill
// 迭代器实现(for)
// Jack Bill
3.foreach循环
import java.util.*;
public class Iterator1{
public static void main(String[] args) {
List<String> list=new LinkedList<String>();
list.add("Jack");
list.add("Bill");
for(String elem:list) {
System.out.printf(elem+" ");
}
}
}
//程序输出结果为
// Jack Bill
4.foreach+Lambda
import java.util.*;
public class Iterator1{
public static void main(String[] args) {
List<String> list=new LinkedList<String>();
list.add("Jack");
list.add("Bill");
list.forEach(elem->{System.out.println(elem);});
}
}
//程序输出结果为
// Jack
// Bill
==============================================================================
参考博客:
java-foreach实现原理
java 自定义类如何实现foreach循环
How does the Java ‘for each’ loop work?
Java Iterator 接口简介和简单用法
本文地址:https://blog.csdn.net/cookie_plus/article/details/107358453