增强for和debug的详细信息介绍

1 Iterator接口

在程序开发中,经常需要遍历集合中的所有元素。针对这种需求,JDK专门提供了一个接口java.util.IteratorIterator接口也是Java集合中的一员,但它与CollectionMap接口有所不同,Collection接口与Map接口主要用于存储元素,而Iterator主要用于迭代访问(即遍历)Collection中的元素,因此Iterator对象也被称为迭代器。

想要遍历Collection集合,那么就要获取该集合迭代器完成迭代操作,下面介绍一下获取迭代器的方法:

  • public Iterator iterator(): 获取集合对应的迭代器,用来遍历集合中的元素的。

下面介绍一下迭代的概念:

  • 迭代:即Collection集合元素的通用获取方式。在取元素之前先要判断集合中有没有元素,如果有,就把这个元素取出来,继续在判断,如果还有就再取出出来。一直把集合中的所有元素全部取出。这种取出方式专业术语称为迭代。

Iterator接口的常用方法如下:

  • public E next():返回迭代的下一个元素。
  • public boolean hasNext():如果仍有元素可以迭代,则返回 true。

接下来我们通过案例学习如何使用Iterator迭代集合中元素:

public class IteratorDemo {
  	public static void main(String[] args) {
        // 使用多态方式 创建对象
        Collection<String> coll = new ArrayList<String>();

        // 添加元素到集合
        coll.add("串串星人");
        coll.add("吐槽星人");
        coll.add("汪星人");
        //遍历
        //使用迭代器 遍历   每个集合对象都有自己的迭代器
        Iterator<String> it = coll.iterator();
        //  泛型指的是 迭代出 元素的数据类型
        while(it.hasNext()){ //判断是否有迭代元素
            String s = it.next();//获取迭代出的元素
            System.out.println(s);
        }
  	}
}

在进行集合元素取出时,如果集合中已经没有元素了,还继续使用迭代器的next方法,将会发生java.util.NoSuchElementException没有集合元素的错误。

2 迭代器的实现原理

我们在之前案例已经完成了Iterator遍历集合的整个过程。当遍历集合时,首先通过调用集合的iterator()方法获得迭代器对象,然后使用hashNext()方法判断集合中是否存在下一个元素,如果存在,则调用next()方法将元素取出,否则说明已到达了集合末尾,停止遍历元素。

Iterator迭代器对象在遍历集合时,内部采用指针的方式来跟踪集合中的元素,为了让初学者能更好地理解迭代器的工作原理,接下来通过一个图例来演示Iterator对象迭代元素的过程:
在这里插入图片描述

在调用Iterator的next方法之前,迭代器的索引位于第一个元素之前,不指向任何元素,当第一次调用迭代器的next方法后,迭代器的索引会向后移动一位,指向第一个元素并将该元素返回,当再次调用next方法时,迭代器的索引会指向第二个元素并将该元素返回,依此类推,直到hasNext方法返回false,表示到达了集合的末尾,终止对元素的遍历。

2.1利用debug的一些小知识

在debug时,我们可以把要选择进行debug的那个代码,然后点进进行,接着在操作框中右键选中那个出现的信息(文中是args = {String[0]@483),然后
选择Mute Renderers,就会出现详细信息了,文中是指自coll 后面的那些信息提示
在这里插入图片描述

Object[0] 是指object型的数组,数组大小为0,
modCount是指版本(每次运行会+1)。
在这里插入图片描述
当把所有信息都加入进去后,size变为3,modCount 也变为3了。
在这里插入图片描述
接着下图中1(那个红色的按钮)是下转 ,功能是转到这个函数的源代码里,下图中2(那个蓝色的按钮)是出来,功能是转出这个函数的源代码,并把debug到下一行在这里插入图片描述
在这里插入图片描述

2.2debug的详细信息介绍

在这里插入图片描述
capacityIncrement 是指增量
elementCount 默认为0

在这里插入图片描述

3. 增强for

增强for循环(也称for each循环)是JDK1.5以后出来的一个高级for循环,专门用来遍历数组和集合的。

格式:

for(元素的数据类型  变量 : Collection集合or数组){ 
  	//写操作代码
}

它用于遍历Collection和数组。通常只进行遍历元素,不要在遍历的过程中对集合元素进行增删操作

练习1:遍历数组
public class NBForDemo1 {
    public static void main(String[] args) {
		int[] arr = {3,5,6,87};
       	//使用增强for遍历数组
		for(int a : arr){//a代表数组中的每个元素
			System.out.println(a);
		}
	}
}
练习2:遍历集合
public class NBFor {
    public static void main(String[] args) {        
    	Collection<String> coll = new ArrayList<String>();
    	coll.add("小河神");
    	coll.add("老河神");
    	coll.add("神婆");
    	//使用增强for遍历
    	for(String s :coll){//接收变量s代表 代表被遍历到的集合元素
    		System.out.println(s);
    	}
	}
}

tips: 新for循环必须有被遍历的目标。目标只能是Collection等或者是数组。新式for仅仅作为遍历操作出现。

4. java.lang.Iterable接口

java.lang.Iterable接口,实现这个接口允许对象成为 “foreach” 语句的目标。

Java 5时Collection接口继承了java.lang.Iterable接口,因此Collection系列的集合就可以直接使用foreach循环遍历。

java.lang.Iterable接口的抽象方法:

  • public Iterator iterator(): 获取对应的迭代器,用来遍历数组或集合中的元素的。

代码示例:

让我们自定义的动态数组支持foreach遍历

自定义动态数组:

import java.util.Arrays;
import java.util.Iterator;
import java.util.NoSuchElementException;

public class MyArrayList<E> implements Iterable<E>{
	private Object[] all;
	private int total;
	
	public MyArrayList(){
		all = new Object[5];
	}

	public void add(E e) {
		ensureCapacityEnough();
		all[total++] = e;
	}

	private void ensureCapacityEnough() {
		if(total >= all.length){
			all = Arrays.copyOf(all, all.length*2);
		}
	}

	//...省略其他代码

	@Override
	public Iterator<E> iterator() {
		return new Itr();
	}
	
	private class Itr implements Iterator<E>{
		int cursor;

		@Override
		public boolean hasNext() {
			return cursor<=total;
		}

		@SuppressWarnings("unchecked")
		@Override
		public E next() {
			return (E) all[cursor++];
		}
		
	}
}

测试类:

public class TestForeach {
	public static void main(String[] args) {
		MyArrayList<String> my = new MyArrayList<String>();
		my.add("hello");
		my.add("java");
		my.add("world");
		my.add("atguigu");
		my.add("list");
		my.add("data");
		
		for (String string : my) {
			System.out.println(string);
		}
	}
}

同理,因为foreach本质上就是使用Iterator迭代器进行遍历的,所以也不要在foreach遍历的过程使用Collection的remove()方法。否则,要么报异常java.util.ConcurrentModificationException,要么行为不确定。

	@Test
	public void test07(){
		Collection<Student> coll = new ArrayList<>();
		coll.add(new Student("陈琦"));
		coll.add(new Student("李晨"));
		coll.add(new Student("邓超"));
		coll.add(new Student("黄晓明"));
		
		//调用ArrayList里面的Iterator iterator()
		for (Student student : coll) {
			if("黄晓明".equals(student.getName())){
				coll.remove(student);
			}
		}
		
		System.out.println(coll);
	}

5. Java中modCount的用法,快速失败(fail-fast)机制

5.1 什么叫快速失败机制

当使用foreach或Iterator迭代器遍历集合时,同时调用迭代器自身以外的方法修改了集合的结构,例如调用集合的add和remove方法时,就会报ConcurrentModificationException。
这样设计是因为,迭代器代表集合中某个元素的位置,内部会存储某些能够代表该位置的信息。当集合发生改变时,该信息的含义可能会发生变化,这时操作迭代器就可能会造成不可预料的事情。因此,果断抛异常阻止,是最好的方法。这就是Iterator迭代器的快速失败(fail-fast)机制。
注意,迭代器的快速失败行为不能得到保证,一般来说,存在不同步的并发修改时,不可能作出任何坚决的保证。快速失败迭代器尽最大努力抛出 ConcurrentModificationException。因此,编写依赖于此异常的程序的方式是错误的,正确做法是:*迭代器的快速失败行为应该仅用于检测 bug。*例如:

	@Test
	public void test02() {
		ArrayList<String> list = new ArrayList<>();
		list.add("hello");
		list.add("java");
		list.add("atguigu");
		list.add("world");
		
        //以下代码没有发生ConcurrentModificationException异常
		Iterator<String> iterator = list.iterator();
		while(iterator.hasNext()){
			String str = iterator.next();
			
			if("atguigu".endsWith(str)){
				list.remove(str);
			}
		}
	}

5.2如何实现快速失败(fail-fast)机制的呢?

  • 在ArrayList等集合类中都有一个modCount变量。它用来记录集合的结构被修改的次数。
  • 当我们给集合添加和删除操作时,会导致modCount++。
  • 然后当我们用Iterator迭代器遍历集合时,创建集合迭代器的对象时,用一个变量记录当前集合的modCount。例如:int expectedModCount = modCount;,并且在迭代器每次next()迭代元素时,都要检查 expectedModCount != modCount,如果不相等了,那么说明你调用了Iterator迭代器以外的Collection的add,remove等方法,修改了集合的结构,使得modCount++,值变了,就会抛出ConcurrentModificationException。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值