Java自我学习路线
一、Iterator概述
- Iterator 接口主要用于遍历Collection集合中的元素,Iterator对象也被称为迭代器(一种设计模式)
- Iterator 仅用于遍历集合,本身并没有盛装对象的能力,如果需要创建Iterator对象,则必须有一个被迭代的集合
- Iterator 在遍历时,迭代变量所获得的是集合元素的值,而不是集合元素本身,且只能单向移动
二、Iterator的使用
- 使用Iterator()方法要求容器返回一个Iterator,第一次调用Iterator的next()方法时,它返回序列的第一个元素,另外Iterator()方法是java.lang.Iterable接口,被Collection继承;
- 使用next()获得序列中的下一个元素;
- 使用hasNext()检查序列中是否还有元素;
- 使用remove()将迭代器新返回的元素删除
- 为什么继承Iterable接口而不直接实现Iterator
“因为Iterator接口的核心方法next()或者hasNext()是依赖于迭代器的当前迭代位置的。 如果Collection直接实现Iterator接口,势必导致集合对象中包含当前迭代位置的数据(指针),当集合在不同方法间被传递时,由于当前迭代位置不可预置,那么next()方法的结果会变成不可预知, 除非再为Iterator接口添加一个reset()方法,用来重置当前迭代位置,但即使这样,Collection也只能同时存在一个当前迭代位置,而Iterable则不然,每次调用都会返回一个从头开始计数的迭代器,且多个迭代器是互不干扰
不同的Collection接口的子接口的实现类返回的Iterator具体类型可能不同,Array可能返回ArrayIterator,Set可能返回 SetIterator,Tree可能返回TreeIterator,但是它们都实现了Iterator接口,因此,客户端不关心到底是哪种 Iterator,它只需要获得这个Iterator接口即可”
1. Iterator()底层源码
package java.util;
import java.util.function.Consumer;
public interface Iterator<E> {
boolean hasNext(); // 如果仍有元素可以迭代,则返回 true
E next(); // 返回迭代的下一个元素
default void remove() {
throw new UnsupportedOperationException("remove");
}
default void forEachRemaining(Consumer<? super E> action) {
Objects.requireNonNull(action);
while (hasNext())
action.accept(next()); // 返回值类型是Object
}
}
2. Collection 底层源码
package java.util;
import java.util.function.Predicate;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
public interface Collection<E> extends Iterable<E> {
int size();
boolean isEmpty();
boolean contains(Object o);
Iterator<E> iterator();
Object[] toArray();
<T> T[] toArray(T[] a);
boolean add(E e);
boolean remove(Object o);
boolean containsAll(Collection<?> c);
boolean addAll(Collection<? extends E> c);
boolean removeAll(Collection<?> c);
default boolean removeIf(Predicate<? super E> filter) {
Objects.requireNonNull(filter);
boolean removed = false;
final Iterator<E> each = iterator();
while (each.hasNext()) {
if (filter.test(each.next())) {
each.remove();
removed = true;
}
}
return removed;
}
boolean retainAll(Collection<?> c);
void clear();
boolean equals(Object o);
int hashCode();
@Override
default Spliterator<E> spliterator() {
return Spliterators.spliterator(this, 0);
}
default Stream<E> stream() {
return StreamSupport.stream(spliterator(), false);
}
default Stream<E> parallelStream() {
return StreamSupport.stream(spliterator(), true);
}
}
3. 迭代/遍历
- 在Map集合中不能用,在所有的Collection以及子类中使用(所有Collection通用的一种方式)
- 存进去什么类型,取出来还是什么类型,只不过在输出的时候会转换成字符串(println调用toString(),重写toString())
- 步骤:
- 获取集合对象的迭代器对象Iterator
- 通过获取的迭代器对象开始迭代/遍历集合
- 迭代器对象Iterator中的方法:
boolean hasNext() 如果仍有元素可以迭代,则返回 true;
E next() 返回迭代的下一个元素
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class Test {
public static void main(String[] args) {
/**
* 对集合Collection进行遍历/迭代
*/
// 创建集合对象
Collection c = new ArrayList();
// 添加元素
c.add("abc");
c.add("345");
c.add(100);
c.add(new Object());
// 1. 获取集合对象的迭代器对象Iterator
Iterator it = c.iterator();
// 2. 通过获取的迭代器对象开始迭代/遍历集合
while (it.hasNext()) {
Object obj = it.next(); // 返回值类型是Object
System.out.println(obj);
}
}
}
4. NoSuchElementException异常
- 一直取,不判断,会出现 java.util.NoSuchElementException 异常
while(true){
Object obj = it.next();
System.out.println(obj);
}
5. ConcurrentModificationException异常
- 当集合的结构发生改变时,迭代器必须重新获取,如果还是用以前老的迭代器,会出现异常:java.util.ConcurrentModificationException==
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class Test {
public static void main(String[] args) {
/**
* 集合结构只要发生改变,迭代器必须重新获取
*/
// 创建集合
Collection c = new ArrayList();
// 此时获取的迭代器指向的是集合中没有元素状态下的迭代器
// 当集合结构发生了改变,迭代器没有重新获取时,调用next()方法时会发生java.util.ConcurrentModificationException异常
Iterator it = c.iterator();
// 添加元素
c.add(1); // Integer类型
c.add(2);
c.add(3);
// 获取迭代器
// Iterator it = c.iterator();
while (it.hasNext()) {
// 编写代码时next()方法返回值类型必须是Object
// 不能 Integer i = it.next();
Object obj = it.next();
System.out.println(obj);
}
}
}
- 在迭代集合元素的过程中,不能调用集合对象的remove方法删除元素 c.remove(o);
否则会出现:java.util.ConcurrentModificationException(本质上也是集合的结构发生改变)
即:在迭代元素的过程当中,一定要使用迭代器Iterator的remove方法,删除元素,不能使用集合自带的remove方法删除元素
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class Test {
public static void main(String[] args) {
/**
* 集合结构只要发生改变,迭代器必须重新获取
*/
Collection c = new ArrayList();
c.add("abc");
c.add("123");
c.add("ABC");
Iterator it = c.iterator();
while(it.hasNext()){
Object o = it.next();
/*
* 删除元素之后,集合的结构发生了变化,应该重新去获取迭代器
* 但是,循环下一次的时候并没有重新获取迭代器,所以会出现异常:java.util.ConcurrentModificationException
* 出现异常根本原因是:集合中元素删除了,但是没有更新迭代器(迭代器不知道集合变化了)
* 不能c2.remove(o); 直接通过集合去删除元素,没有通知迭代器(导致迭代器现在的状态和原集合状态不同)
* 但可以使用迭代器删除,这会自动更新迭代器,并且更新集合(删除集合中的元素)
*/
it.remove(); // 删除的一定是迭代器指向的当前元素
System.out.println(o);
}
System.out.println(c.size()); //0
}
}
三、ListIterator
Iterator可以提供容器类的统一遍历方式,但当集合结构发生变化时,Iterator迭代器必须重新获取,尽管Iterator可以在遍历中使用remove方法删除元素,但是不能解决遍历时修改List的需求,而Iterator接口的子类ListIterator接口解决了这个问题,实现了遍历List的时候同时进行添加,修改的操作,即在Iterator的基础上添加了 prev 系列方法,实现了反向操作,并且添加了add和set方法
- ListIterator:
双向移动(向前/向后遍历),并产生相对于迭代器在列表中指向的当前位置的前一个和后一个元素的索引;
使用set()方法替换它访问过的最后一个元素;
使用add()方法在next()方法返回的元素之前或previous()方法返回的元素之后插入一个元素 - ListIterator与Iterator的区别
ListIterator 和Iterator都有hasNext()和next()方法,可以实现顺序向后遍历,但是ListIterator有hasPrevious()和previous()方法,可以实现逆向/顺序遍历;
ListIterator 可以通过nextIndex()和previousIndex() 方法定位当前的索引位置;
ListIterator 可以在迭代时对集合进行add、set、remove操作,而Iterator迭代器只能在迭代时对集合进行 remove 操作
import java.util.ArrayList;
import java.util.ListIterator;
public class Test {
public static void main(String[] args) {
/**
* ListIterator遍历时进行修改、添加操作
*/
ArrayList<String> a1 = new ArrayList<>();
a1.add("hello");
a1.add("world");
System.out.println(a1); // [hello, world]
ListIterator<String> li = a1.listIterator();
while (li.hasNext()) {
Object obj = li.next();
// 修改元素
if (obj.equals("hello")) {
li.set("hi");
System.out.println(a1); // [hi, world]
}
// 添加元素
li.add("lzj");
}
System.out.println(a1); // [hi, lzj, world, lzj]
}
}