示例:
给定以下类。
class Item {
int nr;
Item(nr) {
this.nr = nr;
// an expensive operation
}
Item next() {
return ...someCondition....
? new Item(nr + 1)
: null;
}
}
其中第一项始终具有nr==1,每个项都确定下一个项,并且您不希望创建不必要的新项。
我可以在客户端代码中使用以下循环do-while-checkNextForNull-getNext模式:
Item item = new Item(1);
do {
// do something with the item ....
} while ((item = item.next()) != null);
使用Java8-Optional,给定的类变为:
class Item {
....
Optional<Item> next() {
return ...someCondition....
? Optional.of(new Item(nr + 1))
: Optional.empty();
}
}
然后,do-while-checkNextForNull-getNext 循环模式变得有些丑陋和冗长:
Item item = new Item(1);
do {
// do something with the item ....
} while ((item = item.next().orElse(null)) != null);
orElse(null)) != null
这部分让人感到不舒服。
我已经寻找了其他类型的循环,但没有找到更好的解决方案。有更简洁的方法吗?
更新:
可以使用 for-each 循环,同时避免空引用(使用空引用被认为是一种不好的实践)。这个解决方案由 Xavier Delamotte 提出,并且不需要 Java8-Optional。
使用通用迭代器的实现:
public class Item implements Iterable<Item>, Iterator<Item> {
int nr;
Item(int nr) {
this.nr = nr;
// an expensive operation
}
public Item next() {
return new Item(nr + 1);
}
public boolean hasNext() {
return ....someCondition.....;
}
@Override
public Iterator<Item> iterator() {
return new CustomIterator(this);
}
}
并且
class CustomIterator<T extends Iterator<T>> implements Iterator<T> {
T currentItem;
boolean nextCalled;
public CustomIterator(T firstItem) {
this.currentItem = firstItem;
}
@Override
public boolean hasNext() {
return currentItem.hasNext();
}
@Override
public T next() {
if (! nextCalled) {
nextCalled = true;
return currentItem;
} else {
currentItem = currentItem.next();
return currentItem;
}
}
}
那么客户端代码就变得非常简单和干净:
for (Item item : new Item(1)) {
// do something with the item ....
}
虽然这可能被视为 Iterator 合同的违反,因为 new Item(1)
对象包含在循环中,而通常,for 循环将立即调用 next() 并跳过第一个对象。换句话说,对于第一个对象,next() 被违反了,因为它返回了第一个对象本身。
Iterable
接口并返回一个迭代器(Iterator
)呢?这样,你只需要实现hasNext()
(即当前的布尔条件)和next
方法,而不是仅仅实现next()
方法。 - Xavier DelamotteIterator
在基于 IO 的源上实现起来非常棘手。为了找出它是否有下一个元素,实际上必须读取并缓存下一个元素。只依赖单个方法的光标式习语实际上更受欢迎。例如,Spliterator
使用这种方法,并加入了额外的技巧。 - Marko TopolnikhasNext
应该是一个没有延迟的无副作用方法的事实。特别要注意需要向客户端发出 I/O 错误信号所带来的困难。 - Marko Topolnik