欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

荐 java迭代器及foreach详解

程序员文章站 2022-04-15 23:13:57
一.迭代器我们知道,在集合框架中,Collection是根接口,而Collection继承了Iterable接口,所以所有实现collection接口的类都可以使用迭代器.在集合框架中,ArrayList,LinkedList,HashSet,TreeSet都可以使用迭代器.我们看看Iterable接口定义了哪些方法.public interface Iterable {//返回实现java.util.Iterator迭代器对象public abstract Iterato...

一.迭代器

我们知道,在集合框架中,Collection是根接口,而Collection继承了Iterable接口,所以所有实现collection接口的类都可以使用迭代器.
荐
                                                        java迭代器及foreach详解
在集合框架中,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语句.荐
                                                        java迭代器及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语句等同于使用迭代器进行遍历,如下图
荐
                                                        java迭代器及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循环。
荐
                                                        java迭代器及foreach详解
我们在自己类中实现迭代器接口时,如果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