Java学习笔记24

在前面大致讲解了Collection这个根接口的知识,让我们知道Collection接口是List、Set和Queue接口的父接口,该接

口里定义的方法既可用于操作Set集合,也可用于操作List和Queue集合。关于Collection下的子接口和实现类在后面

会讲解到,今天我们来看下Iterator接口,如下程序:

public interface Collection<E> extends Iterable<E>

Collection这个根接口继承了Iterable接口,我们来看下Iterable接口中定义的方法:

Iterator<T> iterator();

Iterator接口也是Java集合框架的成员,只是它与Collection系列、Map系列的集合不一样,Collection系列集合、Map

系列集合主要用于盛装其他对象,而Iterator主要用于遍历Collection集合中的元素,Iterator对象也被称为迭代

器。Iterator接口隐藏了各种Collection实现类的底层细节,向应用程序提供了遍历Collection集合元素的统一编程接

口。

关于Iterator接口定义的方法:

boolean hasNext();

hasNext的用法:如果被迭代的集合元素还没有遍历完,返回true。

E next();

next的用法:返回集合里的下一个元素。

default void remove() {
        throw new UnsupportedOperationException("remove");
    }

remove的用法:删除集合里上一次next方法返回的元素。如果迭代器不支持remove操作的话,就会抛出上面的

UnsupportedOperationException异常。

Iterator的用法如下程序:

public class IteratorTest {

	public static void main(String[] args) {
		Collection collection=new HashSet();
		collection.add("Bill");
		collection.add(2);
		collection.add(true);
		Iterator iterator=collection.iterator();
		while(iterator.hasNext()){
			System.out.println(iterator.next());
		}
	}

}

关于HashSet在后续讲解时会提到,有一点需记住HashSet是通过Hash算法来存储集合中的元素的。通过Collection接

口中add方法添

加元素,并通过被迭代的集合来获取Iterator对象,最后分别输出集合的元素。

以下是我们常发生的错误:

public class IteratorTest {

	public static void main(String[] args) {
		Collection collection=new HashSet();
		collection.add("Bill");
		collection.add("Jack");
		collection.add("Sun");
		Iterator iterator=collection.iterator();
		while(iterator.hasNext()){
			String name=(String)iterator.next();
			System.out.println(name);
			if(name.equals("Jack")){
				collection.remove(name);
			}
		}
	}

}

输出:

Bill

Jack

Exception in thread "main" java.util.ConcurrentModificationException

at java.util.HashMap$HashIterator.nextNode(HashMap.java:1429)

at java.util.HashMap$KeyIterator.next(HashMap.java:1453)

at code2.IteratorTest.main(IteratorTest.java:27)

以上程序在进行迭代时,删除第二个元素“Jack”时引发异常,我们来看看ConcurrentModificationException引发异常

的真正原因:

当方法检测到对象的并发修改,但不允许这种修改时,抛出此异常。

例如,某个线程在 Collection 上进行迭代时,通常不允许另一个线性修改该 Collection。通常在这些情况下,迭代的

结果是不确定的。如果检测到这种行为,一些迭代器实现(包括 JRE 提供的所有通用 collection 实现)可能选择抛

出此异常。执行该操作的迭代器称为快速失败 迭代器,因为迭代器很快就完全失败,而不会冒着在将来某个时间任

意发生不确定行为的风险。

注意,此异常不会始终指出对象已经由不同 线程并发修改。如果单线程发出违反对象协定的方法调用序列,则该对

象可能抛出此异常。例如,如果线程使用快速失败迭代器在 collection 上迭代时直接修改该 collection,则迭代器将

抛出此异常。

注意,迭代器的快速失败行为无法得到保证,因为一般来说,不可能对是否出现不同步并发修改做出任何硬性保

证。快速失败操作会尽最大努力抛出 ConcurrentModificationException。因此,为提高此类操作的正确性而编写一个

依赖于此异常的程序是错误的做法,正确做法是:ConcurrentModificationException 应该仅用于检测 bug。

OK,如果我们改成删除第一个元素“Bill”呢?

public class IteratorTest {

	public static void main(String[] args) {
		Collection collection=new HashSet();
		collection.add("Bill");
		collection.add("Jack");
		collection.add("Sun");
		Iterator iterator=collection.iterator();
		while(iterator.hasNext()){
			String name=(String)iterator.next();
			System.out.println(name);
			if(name.equals("Bill")){
				collection.remove(name);
			}
		}
	}

}

输出:

Bill
Exception in thread "main" java.util.ConcurrentModificationException
	at java.util.HashMap$HashIterator.nextNode(HashMap.java:1429)
	at java.util.HashMap$KeyIterator.next(HashMap.java:1453)
	at code2.IteratorTest.main(IteratorTest.java:27)

好吧,又引发异常了,那改成删除最后一个元素呢?

public class IteratorTest {

	public static void main(String[] args) {
		Collection collection=new HashSet();
		collection.add("Bill");
		collection.add("Jack");
		collection.add("Sun");
		Iterator iterator=collection.iterator();
		while(iterator.hasNext()){
			String name=(String)iterator.next();
			System.out.println(name);
			if(name.equals("Sun")){
				collection.remove(name);
			}
		}

	}

}

输出:

Bill

Jack

Sun

以上程序终于删除了最后一个元素。现在我们终于知道,当我们使用Iterator迭代访问Collection集合元素时,

Collection集合里的元素不能被改变,只有通过Iterator的remove方法删除上一次next方法返回的集合元素才可以,否

则将会引发ConcurrentModificationException异常,以上程序,是在遍历最后一个元素后删除最后的元素,其实在使

用next方法遍历到最后的元素后,如果再调用hashNext就会返回false,也就是说遍历到最后一个元素后,迭代就已经

完毕了,也就不存在当使用Iterator迭代访问Collection集合元素时,对集合元素进行改变了。

转载请注明出处:http://blog.csdn.net/hai_qing_xu_kong/article/details/44001581  情绪控_

时间: 2024-08-10 06:28:05

Java学习笔记24的相关文章

Java学习笔记24(Integer类)

这里介绍基本数据类型包装类,Integer是int的包装类, 其他的基本数据类型的包装类的方法和Integer的方法几乎一致,会一种即可全会 基本数据类型包装类的特点:用于在基本数据类型和字符串之间进行转换 这些类属于java的核心类,不需要import Integer类的方法: parseInt方法 示例: 将字符串变成基本类型 package demo; public class IntegerDemo { public static void main(String[] args) { f

Java 学习笔记(2015.7.20~24)

Java 学习笔记(2015.7.20~24) Java Java 学习笔记(2015.7.20~24) 1.StringBuffer与StringBuilder的区别: 2.基本数据类型包装类 3.Date.Calendar.DateFormat等与时间相关的类 1.常用类String 2.正则表达式 3.常用类 StringBuffer 代表可变的字符序列 拆箱与装箱 包装类的应用 匿名内部类 数组 day11 day12 day13 day14 day11 父类中使用public定义的方法

java学习笔记3——java关键字

java学习笔记3——java关键字 虽然老师说不用刻意的去记忆,但是我还是在网上找到了非常详细的注解,再次收藏 关键字的类型表: 各个关键字的详细注解和实例,按首字母排序: 1.abstract abstract 关键字可以修改类或方法. abstract 类可以扩展(增加子类),但不能直接实例化. abstract 方法不在声明它的类中实现,但必须在某个子类中重写. -示例- public abstract class MyClass{ } public abstract String my

【Java学习笔记之二十六】深入理解Java匿名内部类

在[Java学习笔记之二十五]初步认知Java内部类中对匿名内部类做了一个简单的介绍,但是内部类还存在很多其他细节问题,所以就衍生出这篇博客.在这篇博客中你可以了解到匿名内部类的使用.匿名内部类要注意的事项.如何初始化匿名内部类.匿名内部类使用的形参为何要为final. 一.使用匿名内部类内部类 匿名内部类由于没有名字,所以它的创建方式有点儿奇怪.创建格式如下: new 父类构造器(参数列表)|实现接口() { //匿名内部类的类体部分 } 在这里我们看到使用匿名内部类我们必须要继承一个父类或者

Java学习笔记之继承

一.继承的基础 在Java术语中,被继承的类叫超类(superclass),继承超类的类叫子类(subclass). 举例说明: 1 class Box 2 { 3 public double width; 4 public double height; 5 public double depth; 6 7 //重载构造方法 8 public Box(Box ob) 9 { 10 width = ob.width; 11 height = ob.height; 12 depth = ob.dep

Java学习笔记之接口

一.接口的概念与定义 首先考虑一个简单的接口的定义: public interface Output { int MAX_LINE = 40; void out(); void getData(String msg); } 定义接口使用关键字interface 修饰符interface前面的public可以省略,如果省略,则采用默认访问控制,即只有在相同包结构的代码才可以访问此接口 接口不可以有构造方法(区别于类中的构造方法) 接口里面的所有成员,包括常量.方法等都是public访问权限,所以在

java学习笔记 第二篇 核心技术(二)

第十四章 集合类 集合类用来存放对象的引用.继承关系如下图: 14.1 Collection 接口 是层次结构中的根接口,构成Collection的单位称为元素.Collection接口不能直接使用,但该接口提供了添加元素.删除元素.管理数据的方法. Collection接口常用方法: 14.2 List 集合 包括List接口以及List集合的所有实现类.List集合中的元素允许重复,各元素循序就是对象插入的顺序 1.List接口,两个重要方法: get(int index): 获取指定索引位

Java学习笔记之方法重载,动态方法调度和抽象类

一.方法重载 如果子类中的方法与它的超类中的方法有相同的方法名,则称子类中的方法重载超类中的方法,特别是当超类和子类中的方法名和参数类型都相同时,在子类中调用该方法时,超类中的方法会被隐藏.考虑下面程序: 1 class A 2 { 3 int i, j; 4 A(int a, int b) 5 { 6 i = a; 7 j = b; 8 } 9 10 // display i and j 11 void show() 12 { 13 System.out.println("i and j: &

Java学习笔记——动态代理

所谓动态,也就是说这个东西是可变的,或者说不是一生下来就有的.提到动态就不得不说静态,静态代理,个人觉得是指一个代理在程序中是事先写好的,不能变的,就像上一篇"Java学习笔记--RMI"中的远程代理,其中客户端服务对象就是一个远程服务对象的代理,这个代理可以使得客户在操作时感觉像在操作本地对象一样,远程对象对于客户是透明的.我们可以看出这里的远程代理,是在程序中事先写好的,而本节我们要讨论的远程代理,是由JVM根据反射机制,在程序运行时动态生成的.(以上是本人的理解,如果有不正确的地