集合类都支持 foreach
进行迭代。实际上就是调用迭代器进行迭代:
public static void main(String[] args) {
List<String> list = Arrays.asList("A", "B", "C");
for (String s : list) {
System.out.println(s);
}
--------
Iterator var2 = list.iterator(); //这里使用的是List的迭代器在进行遍历操作
while(var2.hasNext()) {
String s = (String)var2.next();
System.out.println(s);
}
}
Iterator迭代器本身也是一个接口,由具体的集合实现类来根据情况实现
public static void main(String[] args) {
List<String> list = Arrays.asList("A", "B", "C");
//通过调用iterator方法快速获取当前集合的迭代器
//Iterator迭代器本身也是一个接口,由具体的集合实现类来根据情况实现
Iterator<String> iterator = list.iterator();
}
通过调用 .next()
方法获取当前迭代器指向的元素并将其指针往后移动一位。
设计的原因是集合类的实现方案众多,不同的实现有不同的遍历方式,而迭代器可以将其进行统一,只需要各个集合类根据自己的情况进行对应实现就行。
Lterable 接口
//注意这个接口是集合接口的父接口,不要跟之前的迭代器接口搞混了
public interface Iterable<T> {
//生成当前集合的迭代器,在Collection接口中重复定义了一次
Iterator<T> iterator();
//Java8新增方法,因为是在顶层接口中定义的,因此所有的集合类都有这个方法
default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
//这个方法会在多线程部分中进行介绍,暂时不做讲解
default Spliterator<T> spliterator() {
return Spliterators.spliteratorUnknownSize(iterator(), 0);
}
}
得益于Iterable提供的迭代器生成方法,实际上只要是实现了迭代器接口的类(我们自己写的都行),都可以使用foreach
语法:
public class Test implements Iterable<String>{ //这里我们随便写一个类,让其实现Iterable接口
@Override
public Iterator<String> iterator() {
return new Iterator<String>() { //生成一个匿名的Iterator对象
@Override
public boolean hasNext() { //这里随便写的,直接返回true,这将会导致无限循环
return true;
}
@Override
public String next() { //每次就直接返回一个字符串吧
return "测试";
}
};
}
}
支持 Lambda 表达式的 forEach
在Java8提供了一个支持Lambda表达式的forEach方法,这个方法接受一个Consumer,也就是对遍历的每一个元素进行的操作:
public static void main(String[] args) {
List<String> list = Arrays.asList("A", "B", "C");
list.forEach(System.out::println);
}
这个效果跟上面的写法是完全一样的,因为forEach方法内部本质上也是迭代器在处理。
default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) { //foreach语法遍历每一个元素
action.accept(t); //调用Consumer的accept来对每一个元素进行消费
}
}
源码与在ArrayList等中的实现
public interface Iterator<E> {
//看看是否还有下一个元素
boolean hasNext();
//遍历当前元素,并将下一个元素作为待遍历元素
E next();
//移除上一个被遍历的元素(某些集合不支持这种操作)
default void remove() {
throw new UnsupportedOperationException("remove");
}
//对剩下的元素进行自定义遍历操作
default void forEachRemaining(Consumer<? super E> action) {
Objects.requireNonNull(action);
while (hasNext())
action.accept(next());
}
}
在ArrayList和LinkedList中,迭代器的实现也不同,比如ArrayList就是直接按下标访问:
public E next() {
...
cursor = i + 1; //移动指针
return (E) elementData[lastRet = i]; //直接返回指针所指元素
}
LinkedList就是不断向后寻找结点:
public E next() {
...
next = next.next; //向后继续寻找结点
nextIndex++;
return lastReturned.item; //返回结点内部存放的元素
}
Listlterator 针对List的迭代器接口
因为List是有序集合,所以它支持两种方向的遍历操作,不仅能从前向后,也可以从后向前:
public interface ListIterator<E> extends Iterator<E> {
//原本就有的
boolean hasNext();
//原本就有的
E next();
//查看前面是否有已经遍历的元素
boolean hasPrevious();
//跟next相反,这里是倒着往回遍历
E previous();
//返回下一个待遍历元素的下标
int nextIndex();
//返回上一个已遍历元素的下标
int previousIndex();
//原本就有的
void remove();
//将上一个已遍历元素修改为新的元素
void set(E e);
//在遍历过程中,插入新的元素到当前待遍历元素之前
void add(E e);
}
应用:
public static void main(String[] args) {
List<String> list = new ArrayList<>(Arrays.asList("A", "B", "C"));
ListIterator<String> iterator = list.listIterator();
iterator.next();
iterator.set("X");
System.out.println(list);
}
这种迭代器因为能够双向遍历,所以说可以反复使用。