在一次采访中有人问我,使用迭代器而不是for
循环的优点是for
什么?
有人可以回答吗?
在一次采访中有人问我,使用迭代器而不是for
循环的优点是for
什么?
有人可以回答吗?
Iterator
?
Answers:
首先,有两种for循环,它们的行为非常不同。一种使用索引:
for (int i = 0; i < list.size(); i++) {
Thing t = list.get(i);
...
}
这种循环并非总是可能的。例如,列表具有索引,而集合没有索引,因为它们是无序集合。
另一个foreach循环在幕后使用Iterator:
for (Thing thing : list) {
...
}
这适用于每种Iterable集合(或数组)
最后,您可以使用Iterator,它也可以与任何Iterable一起使用:
for (Iterator<Thing> it = list.iterator(); it.hasNext(); ) {
Thing t = it.next();
...
}
因此,实际上您有3个循环要比较。
您可以用不同的术语来比较它们:性能,可读性,易错性,功能。
迭代器可以执行foreach循环无法执行的操作。例如,如果迭代器支持,则可以在迭代时删除元素:
for (Iterator<Thing> it = list.iterator(); it.hasNext(); ) {
Thing t = it.next();
if (shouldBeDeleted(thing) {
it.remove();
}
}
列表还提供可以双向迭代的迭代器。foreach循环仅从头到尾进行迭代。
但是,迭代器更危险,可读性更差。当您只需要一个foreach循环时,它就是最易读的解决方案。使用迭代器,您可以执行以下操作,这将是一个错误:
for (Iterator<Thing> it = list.iterator(); it.hasNext(); ) {
System.out.println(it.next().getFoo());
System.out.println(it.next().getBar());
}
foreach循环不允许发生此类错误。
对于数组支持的集合,使用索引访问元素的效率稍高。但是,如果您改变主意并使用LinkedList而不是ArrayList,则性能会突然变差,因为每次访问时list.get(i)
,链表都必须循环遍历所有元素,直到第i个元素为止。迭代器(以及foreach循环)不存在此问题。它始终使用最佳方法来遍历给定集合的元素,因为集合本身具有自己的Iterator实现。
我的一般经验法则是:使用foreach循环,除非您确实需要Iterator的功能。当我需要访问循环内的索引时,我只会使用带有数组索引的for循环。
迭代器优势:
next()
和previous()
。hasNext()
。循环仅设计为对a进行迭代Collection
,因此,如果只想对a进行迭代Collection
,则最好使用,例如for-Each
,但如果希望更多,则可以使用Iterator。
ListIterator
您还可以add
在任意点开始迭代。
如果按数字访问数据(例如“ i”),则使用数组时速度很快。因为它直接进入元素
但是,其他数据结构(例如树,列表)需要更多时间,因为它从第一个元素开始到目标元素。使用清单时。它需要时间O(n)。因此,它要慢一些。
如果使用迭代器,则编译器会知道您所在的位置。所以它需要O(1)(因为它从当前位置开始)
最后,如果仅使用支持直接访问的数组或数据结构(例如,java中的arraylist)。“ a [i]”很好。但是,当您使用其他数据结构时,迭代器效率更高
Collections
。值得指出的是,增强的for循环Iterator
在幕后使用。
除了可以访问或不访问要迭代的项目的索引这一明显的区别之外,Iterator和经典的for循环之间的主要区别在于,使用Iterator可以从底层集合实现中抽象出客户端代码,阐述。
当您的代码使用迭代器时,采用以下形式
for(Item element : myCollection) { ... }
这种形式
Iterator<Item> iterator = myCollection.iterator();
while(iterator.hasNext()) {
Item element = iterator.next();
...
}
或这种形式
for(Iterator iterator = myCollection.iterator(); iterator.hasNext(); ) {
Item element = iterator.next();
...
}
您的代码说的是“我不在乎集合的类型及其实现,我只是在乎可以迭代其元素”。通常这是更好的方法,因为它会使您的代码更加分离。
另一方面,如果您使用经典的for循环,例如
for(int i = 0; i < myCollection.size(); i++) {
Item element = myCollection.get(i);
...
}
您的代码说,我需要知道集合的类型,因为我需要以特定的方式遍历集合的元素,因此我还可能要检查空值或根据迭代顺序计算一些结果。这使您的代码更加脆弱,因为如果在任何时候您收到的集合类型发生变化,都会影响代码的工作方式。
总结起来,区别不在于速度或内存使用,更多的在于解耦代码,以便更灵活地应对更改。
与其他答案不同,我想指出另一件事。
如果您需要在代码中的多个位置执行迭代,则最终可能会重复逻辑。显然,这不是一个非常可扩展的方法。相反,需要的是一种从实际处理代码的代码中分离出用于选择数据的逻辑的方法。
一个迭代诸如基于阵列被隐藏-通过用于循环在一组数据,使得底层数据结构或存储机制提供的通用接口来解决这些问题。
CopyOnWriteArrayList
但是它是众所周知的,并且经常被使用,因此值得一提。这是来自这本书的https://www.amazon.com/Beginning-Algorithms-Simon-Harris/dp/0764596748