`
tianlihu
  • 浏览: 313157 次
  • 性别: Icon_minigender_1
  • 来自: 石家庄
社区版块
存档分类
最新评论

在集合类的遍历时操作需要谨慎

阅读更多
本文版权归作者所有,仅供用来网上学习来用,读者可以收藏,请不要下载到本机或重新发布到其它网站

先看一个例子:
import java.util.ArrayList;
import java.util.List;

public class CollectionOperation {

	public static void main(String[] args) {
		List<String> list = new ArrayList<String>();
		for (int i = 0; i < 10; i++) {
			list.add(String.valueOf(i));
		}

		for (String str : list) {
			if (str.equals("5")) {
				list.remove(str);
			}
		}
	}
}

   这个程序可以通过编译,但在运行时会出现异常,异常信息如下:
    Exception in thread "main" java.util.ConcurrentModificationException
    at java.util.AbstractList$Itr.checkForComodification(AbstractList.java:372)
    at java.util.AbstractList$Itr.next(AbstractList.java:343)
    at CollectionOperation.main(CollectionOperation.java:12)

    为什么会出现这种异常呢?因为在执行到第14行时,list的长度发生了变化,由原来的10变为9。第12行的for (String integer : list)这种循环是在JDK5以后才支持的。而这种循环对于集合类来说,会生成一个迭代对象Iterator,然后进行遍历的。在每次循环之前,它会检查集合类当前的长度是否发了变化,如果发生变化就会抛出如上的异常。对于ArrayList, 这段检查的代码是这样的(此段代码是写在AbstractList类的内部类Itr中):
public E next() {
	checkForComodification();
	try {
		E next = get(cursor);
		lastRet = cursor++;
		return next;
	} catch (IndexOutOfBoundsException e) {
		checkForComodification();
		throw new NoSuchElementException();
	}
}

final void checkForComodification() {
	if (modCount != expectedModCount)
		throw new ConcurrentModificationException();
}



  因此,这种JDK5新规范的循环的写法,虽然简单,但是如果有对集合类对象有添加和删除的操作时,会出现这种异常。这时需要用常归的循环的写法了。
import java.util.ArrayList;
import java.util.List;

public class CollectionOperation {

	public static void main1(String[] args) {
		int size = 10;
		List<String> list = new ArrayList<String>();
		for (int i = 0; i < size; i++) {
			list.add(String.valueOf(i));
		}

		for (int i = 0; i < size; i++) {
			String str = list.get(i);
			if (str.equals("5")) {
				list.remove(str);
			}
		}
	}
}

   我们写程序一般很少在遍历的时候添加元素,多数是在遍历的时候,找到匹配的元素,然后进行删除操作。就像上面的例子一样,但是细心的朋友会发现,上面的例子有隐患存在。重新更改一下上面的例子,这个隐患就会暴露出来。
import java.util.ArrayList;
import java.util.List;

public class CollectionOperation {

	public static void main1(String[] args) {
		List<String> list = new ArrayList<String>();
		for (int i = 0; i < 10; i++) {
			list.add(String.valueOf(5));
		}

		for (int i = 0; i < list.size(); i++) {
			String str = list.get(i);
			if (str.equals("5")) {
				list.remove(str);
			}
		}

		for (int i = 0; i < list.size(); i++) {
			System.out.println(list.get(i));
		}
	}
}

  只更改了一下第10行的值为5,也就是List中存放的都是5,对于后面的删除操作,我们本来是打算删除列表中值等于5的元素,即删除List中的全部元素,运行一下程序,我们发现,并没有完全删除,而是剩下了5个元素。
  再看一个更奇怪的例子,还是这样的循环,我们删除一个数列上偶数位上的数,只留奇数位上的数。例如:原数列为10、20、30、40、50、60、70、80、90、100,则偶数位上的数为20、40、60、80、100,删除后剩余的数为:10、30、50、70、90。一般人会写出下面的错误程序:
public class CollectionOperation {

	public static void main(String[] args) {
		List<Integer> list = new ArrayList<Integer>();
		for (int i = 0; i < 10; i++) {
			list.add((i + 1) * 10);
		}

		for (int i = 0; i < list.size(); i++) {
			if ((i + 1) % 2 == 0) {
				list.remove(i);
			}
		}

		for (int i = 0; i < list.size(); i++) {
			System.out.println(list.get(i) +" ");
		}
	}
}

   猛一看这个程序,是对的,可是一运行出来结果就不对。这个程序的结果是:
   10 30 40 60 70 90 100

   这个结果并不是我们想要的,但是到底哪里出错了?因为是因为集合类List为可变长的数组,当删除中间一个元素后,这个被删除元素的后面的元素会向前移动一个,即元素的索引变小了,但是循环变量i是没有变化的,这时再向后遍历时,会漏掉刚删除那个元素上新移进的元素。这样一直往复,直到结束。
   怎样解决这个问题呢?
   改换一下思路,因为删除元素的时候,后面的元素都是向前移动,因此,我们这样循环:
for (int i = list.size(); i >= 0; i--) {
	if ((i + 1) % 2 == 0) {
		list.remove(i);
	}
}

  再试一下,结果正确了:
  10 30 50 70 90

  这种循环很常见,也很有效果,且效率很高。因为List的size方法只被调用了一次。在JDK的原码中,很多listener的遍历都是用的倒序。
0
0
分享到:
评论

相关推荐

    整数集合类的各种操作

    8. **集合的补集**:获取第一个集合中不包含在第二个集合的所有元素,这需要先创建一个全集,然后用差集操作来实现。 ```java Set&lt;Integer&gt; universal = new HashSet(set1); universal.addAll(set2); Set&lt;Integer&gt; ...

    C++编程:建造集合类实训

    本文将详细讲解C++编程中集合类的实现,包括集合类的定义、添加元素、删除元素、判断集合是否为空或满、判断元素是否包含在集合中、计算集合中的元素总数、输出集合中的元素、求两个集合的交集和并集等操作。...

    java集合类线程安全.doc

    本文将结合上述 Bloch 关于线程安全等级的定义,对 Java 集合框架中的集合类进行线程安全性分析,并指出各个集合类在现实的编程环境中需要注意的并发编程的陷阱;同时对集合框架中通用算法对线程安全性的影响进行...

    java集合类详解(set list ArrayList等java集合类详述)

    Java 集合类详解 Java 集合类是 Java 语言中的一种基本数据结构,用于存储和...在 Java 中,集合类是非常重要的,它们可以帮助我们更方便地存储和操作大量数据。了解集合类的特点和用法可以帮助我们更好地编写代码。

    C#集合遍历时删除和增加元素的方法

    本文实例讲述了C#集合遍历时删除和增加元素的方法。分享给大家供大家参考,具体如下: 大多数时候,遍历集合元素的时候并不需要对元素进行增加或者删除操作,但有些时候则需要,比如,如果集合中盛放的元素是社会上...

    大公司最喜欢问的Java集合类面试题

    - 在遍历时不应该修改集合内容(除了`Iterator`的`remove()`方法)。 - 如果在多线程环境中使用,需要同步遍历代码,以防止并发修改异常。 #### 四、实现类详解 - **LinkedList**: - 实现了`List`接口。 - 支持...

    多映射集合类

    在IT行业中,集合类是数据结构的一个重要组成部分,它们提供了组织和操作数据的方便接口。在C#编程语言中,标准库提供了丰富的集合类,如List、Dictionary, TValue&gt;等。然而,有时我们需要处理一个键可以对应多个值...

    java集合类详解

    Java集合类是Java语言中用来存储...整个集合框架非常庞大,本文仅就部分概念和类进行了详细说明,实际上,不同的集合类在性能、线程安全、存储结构等方面都有所不同,开发者应该根据实际需求选择合适的集合类来使用。

    Cpp集合类.rar

    在C++编程中,集合类是STL(标准模板库)的一部分,用于高效地管理和操作数据集合。在微软的MFC(Microsoft Foundation Classes)库中,也提供了集合类的实现,便于开发者处理对象的集合。"Cpp集合类.rar"这个压缩包...

    java集合类演示源码

    实现Comparable接口或Comparator接口,用户可以根据需要对集合中的元素进行排序。为了方便用户使用,Java平台还提供了Collections和Arrays工具类。collection.rar分别对上述内容进行详细讲解演示。

    C#集合类 教程学习资源

    在C#编程中,集合类是用于存储和管理对象的关键组件。C#的集合类主要分布在几个不同的命名空间中,包括System.Collections、System.Collections.Generic和System.Collections.Specialized。这些命名空间提供了不同...

    C++集合类的编写,用数组实现的

    在C++编程中,集合类是一种常见的数据结构,它提供了对一组特定对象的管理功能,如添加、删除、查找和操作这些对象。本程序通过数组实现集合类,这是对基本数据结构的一种简单而直观的实现方式。下面我们将深入探讨...

    java集合类总结

    在Java中,集合主要分为两大类:Collection和Map。本文将对Java集合框架中的Collection接口及其相关接口、类进行深入的探讨。 首先,Collection接口是所有单值容器的基础,它是Set、List和Queue接口的父接口。...

    Java集合类API.pdf

    在实际开发中,需要根据具体需求选择最合适的集合类,并考虑到线程安全和性能的影响。同时,随着Java版本的更新,新的并发工具和数据结构如ConcurrentHashMap的出现,使得在多线程环境下也能实现高性能的数据操作。...

    JAVA集合类用法总结

    开发者可以根据需求选择最适合的集合类型,比如在需要高效查找的场景下选择ArrayList,而在处理大量插入和删除操作时选择LinkedList。此外,Set接口用于存储无序且不重复的元素,适合去重或表示一组独特的对象。Map...

    java 集合类讲解

    在实际编程中,选择合适的集合类和操作方式对于程序的性能至关重要。比如,如果需要频繁进行元素的添加和删除,`LinkedList`可能是更好的选择;如果对元素的顺序有特定要求,`ArrayList`或`TreeSet`可能更适合;如果...

    集合类的菜单界面

    在C++编程中,集合类(Set)是一种用于存储唯一对象的数据结构,它不包含重复元素。在本项目中,我们关注的是一个基于数组实现的简单整数集合,提供了诸如交集、并集等基本操作的菜单界面。下面将详细讨论相关知识点...

    Java集合类矩阵图

    在这个矩阵图中,你可以看到ArrayList、LinkedList、HashSet、HashMap等常见集合类的特性、性能以及它们在不同场景下的适用性。 Java集合框架是Java标准库(java.util包)的一部分,用于存储和管理对象。它的核心是...

Global site tag (gtag.js) - Google Analytics