Iterator和Enumeration的区别
我们通常用Iterator(迭代器)或者Enumeration(枚举器)去遍历集合, 那么这个类有何区别?
直接上源码:
package java.util
public interface Enmeration {
boolean hasMoreElements();
E nextElement();
}
package java.util
public interface Iterator {
boolean hasNext();
E next();
void remove(); **//迭代器这里比枚举器多一个方法哦.**
}
迭代器和枚举器的区别
- 枚举器(Enumberation)只提供了两个接口方法, 意味着我们只能读取数据.
- 迭代器(Iterator)在枚举器基础了又提供了一个对数据删除的操作.
- Iterator支持'Fail-Fast机制', 而Enumeration不支持.
- 枚举遍历的效率比迭代器效率高.
fail-fast简介
fail-fast机制是java集合(Collection)中的一中错误机制, 当多个线程对同一个集合的内容进行操作时, 就可能产生fail-fast事件.
如:
当一个线程A通过iterator去遍历某集合的过程中, 若该集合的内容被其他线程改变了, 那么线程A访问集合时, 就会抛出ConcurrentModificationException异常,产生fail-fast事件.简单点说:线程A遍历集合list的过程中, list的内容被另外一个线程所改变了.就会抛出ConcurrentModificationException异常,即产生fail-fast事件.
fail-fast解决办法
fail-fast机制,是一种错误检测机制, 它只能被用来检测错误, 以为JDK并不保证fail-fast机制一定会发生.
具体分析:
产生fail-fast事件,是通过抛出ConcurrentModificationException异常来触发的。
那么,ArrayList是如何抛出ConcurrentModificationException异常的呢?
我们知道,ConcurrentModificationException是在操作Iterator时抛出的异常。我们先看看
Iterator的源码。ArrayList的Iterator是在父类AbstractList.java中实现的。代码如下:
package java.util;
public abstract class AbstractList extends AbstractCollection implements List {
...
// AbstractList中唯一的属性
// 用来记录List修改的次数:每修改一次(添加/删除等操作),将modCount+1
protected transient int modCount = 0;
// 返回List对应迭代器。实际上,是返回Itr对象。
public Iterator iterator() {
return new Itr();
}
//在AbstractList类中找到了Itr类的具体实现,它是AbstractList的一个成员内部类,
// Itr是Iterator(迭代器)的实现类
private class Itr implements Iterator {
//cursor:表示下一个要访问的元素的索引,从next()方法的具体实现就可看出
int cursor = 0;
//lastRet:表示上一个访问的元素的索引
int lastRet = -1;
//expectedModCount:表示对ArrayList修改次数的期望值,它的初始值为modCount。
// 修改数的记录值。
// 每次新建Itr()对象时,都会保存新建该对象时对应的modCount;
// 以后每次遍历List中的元素的时候,都会比较expectedModCount和modCount是否相等;
// 若不相等,则抛出ConcurrentModificationException异常,产生fail-fast事件。
int expectedModCount = modCount;
public boolean hasNext() {
return cursor != size();
}
public E next() {
// 获取下一个元素之前,都会判断“新建Itr对象时保存的modCount”和“当前的modCount”是否相等;
// 若不相等,则抛出ConcurrentModificationException异常,产生fail-fast事件。
checkForComodification();
try {
E next = get(cursor);
lastRet = cursor++;
return next;
} catch (IndexOutOfBoundsException e) {
checkForComodification();
throw new NoSuchElementException();
}
}
public void remove() {
if (lastRet == -1)
throw new IllegalStateException();
checkForComodification();
try {
AbstractList.this.remove(lastRet);
if (lastRet < cursor)
cursor--;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException e) {
throw new ConcurrentModificationException();
}
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
...
}
从中,我们可以发现在调用 next() 和 remove()时,都会执行 checkForComodification()。若 “modCount 不等于 expectedModCount”,则抛出ConcurrentModificationException异常,产生fail-fast事件。
1. 在单线程下,细心的朋友可能发现在Itr类中也给出了一个remove()方法:
public void remove() {
if (lastRet == -1)
throw new IllegalStateException();
checkForComodification();
try {
AbstractList.this.remove(lastRet);
if (lastRet < cursor)
cursor--;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException e) {
throw new ConcurrentModificationException();
}
}
在这个方法中,删除元素实际上调用的就是list.remove()方法,但是它多了一个操作:
expectedModCount = modCount;
因此,在迭代器中如果要删除元素的话,需要调用Itr类的remove方法。
代码举例:
public class Test {
public static void main(String[] args) {
ArrayList list = new ArrayList();
list.add(2);
Iterator iterator = list.iterator();
while(iterator.hasNext()){
Integer integer = iterator.next();
if(integer==2)
iterator.remove(); //注意这个地方
}
}
}
2. 如果在多线程编程下
- 最好用java.util.concurrent包下的类去取代java.util包下的类.
- 1)在使用iterator迭代的时候使用synchronized或者Lock进行同步;
- 2)使用并发容器CopyOnWriteArrayList代替ArrayList和Vector。
数组和集合排序
public class Person implements Comparable{
private String name;
private int age;
@Override
public int compareTo(Person o) {
if(o.age > this.age) return 1;
return -1;
}
}
public static void main(String[] args) {
Person[] persons = new Person[10];
for (int i = 0; i < 10; i++) {
Person person = new Person("daejong", (new Random()).nextInt(30)+1);
persons[i] = person;
}
//Person实现了Comparable的内部排序法.
Arrays.sort(persons);
//指定一个比较器, 很灵活的进行排序, 外部排序法
Arrays.sort(persons, new Comparator() {
@Override
public int compare(Person o1, Person o2) {
if(o1.getAge() > o2.getAge()) return 1;
return -1;
}
});
}
集合Collections是专门处理容器的类,该类的Collections.sort()类似Arrays.sort()只是作用的
对象不一样而已.
/*
* Collections类似Arrays, 是对Collection类的一些类方法,HashMap, treeMap, HashSet, treeSet, ArrayList, LinkedList
* 附加注意:
*
Map.Entry
Iterator
Enumeration
HashMap: 最多允许一条记录的键为null, 允许多条记录的值为null.
Hashtable: 不允许键或值为空.
LinkedHashMap: 保存了插入的顺序.
TreeMap: 默认按照键升序排序.
*
Collections.sort(List list); //T中实现内部排序接口
Collections.sort(List Comparator super T>c); //执行一个比较器 进行排序.
* */
排序总结
/*
* Comparator和Comparable比较
1.Comparable是排序接口, 若一个类实现了Comparable接口, 就意味着该类支持排序. 要实现CompareTo(T o)方法
2.而Comparator是比较器, 我们如果需要控制某个类的次序, 可以创建一个该类的比较器来进行排序. 要实现Compare(T o1, T o2)方法.
3. 不难发现, Comparable相当于'内部比较器', Comparator相当于'外部比较器'.
* */
/*
* Arrays排序都是经过调优的快速排序
* 1. 对基本类型的数组进行排序, Arrays提供了默认的升序排序,没有提供相应的降序排序
* 2. 若要对基本数据类型进行降序排序, 需要将这些数组转化为对应的封装类数组. 如Integer[], Double[], Character[]
* */