java迭代器及foreach详解
一.迭代器 我们知道,在集合框架中,Collection是根接口,而Collection继承了Iterable接口,所以所有实现collection接口的类都可以使用迭代器.
文章图片
在集合框架中,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语句.
文章图片
声明的循环变量的类型必须和数组的类型相同
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语句等同于使用迭代器进行遍历,如下图
文章图片
两种形式可以相互转换
我们在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循环。
文章图片
我们在自己类中实现迭代器接口时,如果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 接口简介和简单用法
推荐阅读
- JAVA(抽象类与接口的区别&重载与重写&内存泄漏)
- Docker应用:容器间通信与Mariadb数据库主从复制
- 人生感悟记#环境仪器宋庆国成长记#072
- 标签、语法规范、内联框架、超链接、CSS的编写位置、CSS语法、开发工具、块和内联、常用选择器、后代元素选择器、伪类、伪元素。
- 事件代理
- Java|Java OpenCV图像处理之SIFT角点检测详解
- java中如何实现重建二叉树
- 视频转换器哪种好用()
- 数组常用方法一
- NeuVector 会是下一个爆款云原生安全神器吗()