一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第1天,点击查看活动详情。
概要
学习java的Collection体系之前先学习一下Iterable是有必要的, 不仅仅因为它是Collection的唯一父接口,同时也因为他是java for-each的'实现'.
code
public interface Iterable<T> {
Iterator<T> iterator();
default void forEach(Consumer<? super T> action) {...}
default Spliterator<T> spliterator() {...}
}
Iterable的代码是非常简单的,在1.8之前只有iterator这么一个抽象方法, 1.8之后才提供了stream or forEach的语义支持. 但是iterator这个方法缺不可谓不重要, 甚至改变了java for循环的生态,是一个十分强大的特性,因为所有实现了Iterable的接口都可以用for-each简化for循环, 我想没几个人会喜欢手动使用迭代器的便利方式.
Iterable的好兄弟Iterator
public interface Iterator<E> {
boolean hasNext();
E next();
default void remove() {throw new UnsupportedOperationException("remove"); }
default void forEachRemaining(Consumer<? super E> action) {...}
}
Iterator的代码也非常简单,java1.5之前想要遍历LinkedList我想应该都得依靠它了,1.5之后普通的遍历虽然可以使用for-each语法,但在1.8之前如果想要在遍历的过程中删除元素也得使用它. 而1.8之后Iterator应该就慢慢退出非基础库开发者的视野了,毕竟for-each和stream太好用了. 但现在有一个方法提前引起重视: 在接口中声明一个remove方法然后默认实现抛出UnsupportedOperationException, 这种操作是反直觉的,毕竟当你不支持一种操作时更好的方式不应该是不声明这个方法吗. 事实上这种设计在诸多java基础库中时非常常见的,我认为这是java亦或面向对象设计的一个缺陷,想要避免类膨胀不得已而为之的一种错误设计.
让code跑起来
Iterable是java对象支持for-each语法的基石,现在我们尝试自己做个最简实现. 这里简单实现了一个可迭代的IntRange,可以对一个数字范围进行遍历.
import java.util.Iterator;
public class IterableExample {
public static void main(String[] args) {
for (Integer i : new IntRange(1, 10)) {
System.out.println(i);
}
}
public record IntRange(int start, int end) implements Iterable<Integer> {
@Override
public Iterator<Integer> iterator() {
return new Itr();
}
public class Itr implements Iterator<Integer> {
private int current;
public Itr() {
// 这里会发生越界 Integer.MIN_VALUE - 1
this.current = start - 1;
}
@Override
public boolean hasNext() {
return current < end;
}
@Override
public Integer next() {
return ++current;
}
}
}
}