I think it is entirely legitimate - an Iterator is just a stream of "stuff". Why should the stream necessarily be bounded?
Plenty of other languages (e.g. Scala) have the concept of unbounded streams built in to them and these can be iterated over. For example, using scalaz
The whole point of an Iterator is that it is lazy, i.e. that it gives you only as many objects as you ask for. If a user asks for all objects of an infinite Iterator, it's their problem, not yours.
访问 Iterator 的内部 List 并直接删除对象,这可能会引发 ConcurrentModificationException
使用 break 退出循环
“使用”Exception 退出循环
使用 return 退出循环
除最后一个之外的所有内容这些替代方案并不完全是留下增强型 for 循环的最佳方式。
当然,“普通”for 循环(或任何其他带有显式迭代器变量的循环)都可以正常工作:
for (Iterator it = getIterator(); it.hasNext();) {
Object o = it.next()
...
}
While I too think that it's legitimate, I'd like to add that such an Iterator (or more precisely: an Iterable producing such an Iterator) would not play well with the enhanced for-loop (a.k.a for-each-loop):
for (Object o : myIterable) {
...
}
Since the code inside an enhanced for loop has no direct access to the iterator, it couldn't call remove() on the iterator.
So to end the looping it would have to do one of the following:
Get access to the internal List of the Iterator and remove objects directly, possibly provoking a ConcurrentModificationException
use break to exit the loop
"use" an Exception to exit the loop
use return to leave the loop
All but the last of those alternatives aren't exactly the best way to leave an enhanced for-loop.
The "normal" for-loop (or any other loop together with an explicit Iterator variable) would work just fine, of course:
for (Iterator it = getIterator(); it.hasNext();) {
Object o = it.next()
...
}
This may be semantics, but the iterator should diligently return the next item without regard to when it will end. Ending is a side effect for a collection, though it may seem like a common side effect.
Still, what's the difference between infinite, and a collection with 10 trillion items? The caller either wants them all, or he doesn't. Let the caller decide how many items to return or when to end.
I wouldn't say the caller couldn't use the for-each construct. He could, as long as he wants all the items.
Something in the documentation for the collection like "may be an infinite collection" would be appropriate, but not "infinite iterator".
It is a perfectly legitimate use - as long as it is properly documented.
Using the name CyclicIterator is a good idea, as it infers that looping on the iterator might well possibly be infinite if the loop exit case is not properly defined.
Actually, an Iterator comes from an Iterable, not a Collection, whatever the JavaDoc API says. The latter extends the former, and that's why it can generate an Iterator as well, but it's perfectly reasonably for an Iterable not to be a Collection. In fact, there are even a few Java classes which implement Iterable but not Collection.
Now, as for expectations... of course an infinite iterator must not be made available without a clear indication that this happens to be the case. But, sometimes, expectations can be deceiving. For instance, one might expect an InputStream to always come to an end, but that really isn't a requirement of it. Likewise, an Iterator returning lines read from such a stream might never end.
I can imagine any number of uses for an infinite iterator. Like, what about a program iterating through status messages being sent by another server or a background process. While in real life the server will presumably stop eventually, from the program's point of view it might just keep reading until it is stopped by something unrelated to the iterator reaching its end.
It would certainly be unusual and, as others have said, should be carefully documented. But I wouldn't declare it unacceptable.
If you have an object that needs an iterator, and you happen to give it an infinite iterator, then all should be well. Of course, that object should never require the iterator to stop. And that is the potentially bad design.
Think of a music player. You tell it to start playing tracks in "continuous play mode" and it plays tracks until you tell it to stop. Maybe you implement this continuous play mode by shuffling a playlist forever... until the user presses "stop". In this case, MusicPlayer needs to iterator over a playlist, that might be Collection<Track>, forever. In this case, you'd want either a CyclicIterator<Track> as you've built or a ShuffleIterator<Track> that randomly picks the next track and never runs out. So far, so good.
MusicPlayer.play(Iterator<Track> playlist) wouldn't care whether the play list ends or not. It works both with an iterator over a list of tracks (play to the end, then stop) and with a ShuffleIterator<Track> over a list of tracks (pick a random track forever). All still good.
What if someone tried to write MusicPlayer.playContinuously()--which expects to play tracks "forever" (until someone presses "stop"), but accepts an Iterator<Track> as the parameter? That would be the design problem. Clearly, playContinuously() needs an infinite Iterator, and if it accepts a plain Iterator as its parameter, then that would be bad design. It would, for example, need to check hasNext() and handle the case where that returns false, even though it should never happen. Bad.
So implement all the infinite iterators you want, as long as their clients don't depend on those iterators ending. As if your client depends on the iterator going on forever, then don't give it a plain Iterator.
This should be obvious, but it doesn't seem to be.
发布评论
评论(11)
我认为它是完全合法 -
迭代器
只是一个“东西”流。为什么流必须是有界的?许多其他语言(例如 Scala)都内置了无界流的概念,并且可以对其进行迭代。例如,使用 scalaz
编辑: 就最小惊喜原则而言,我认为这完全取决于上下文。例如,我希望这个方法返回什么?
I think it is entirely legitimate - an
Iterator
is just a stream of "stuff". Why should the stream necessarily be bounded?Plenty of other languages (e.g. Scala) have the concept of unbounded streams built in to them and these can be iterated over. For example, using scalaz
EDIT: In terms of the principle of least surprise, I think it depends entirely on the context. For example, what would I expect this method to return?
Iterator
的全部要点是它是惰性的,即它只提供您所要求的对象数量。如果用户请求无限Iterator
的所有对象,这是他们的问题,而不是你的问题。The whole point of an
Iterator
is that it is lazy, i.e. that it gives you only as many objects as you ask for. If a user asks for all objects of an infiniteIterator
, it's their problem, not yours.虽然我也认为它是合法的,但我想补充一点,这样一个
Iterator
(或更准确地说:一个Iterable
生成这样一个Iterator
) 无法与增强型 for 循环(又名 for-each-loop)配合使用:由于增强型 for 循环内的代码无法直接访问迭代器,因此无法调用
remove()
在迭代器上。因此,要结束循环,必须执行以下操作之一:
Iterator
的内部List
并直接删除对象,这可能会引发ConcurrentModificationException
break
退出循环Exception
退出循环return
退出循环除最后一个之外的所有内容这些替代方案并不完全是留下增强型 for 循环的最佳方式。
当然,“普通”for 循环(或任何其他带有显式迭代器变量的循环)都可以正常工作:
While I too think that it's legitimate, I'd like to add that such an
Iterator
(or more precisely: anIterable
producing such anIterator
) would not play well with the enhanced for-loop (a.k.a for-each-loop):Since the code inside an enhanced for loop has no direct access to the iterator, it couldn't call
remove()
on the iterator.So to end the looping it would have to do one of the following:
List
of theIterator
and remove objects directly, possibly provoking aConcurrentModificationException
break
to exit the loopException
to exit the loopreturn
to leave the loopAll but the last of those alternatives aren't exactly the best way to leave an enhanced for-loop.
The "normal" for-loop (or any other loop together with an explicit
Iterator
variable) would work just fine, of course:这可能是语义上的,但迭代器应该努力返回下一个项目,而不考虑它何时结束。结束是集合的副作用,尽管它可能看起来是常见的副作用。
不过,无限与包含 10 万亿项的集合有什么区别?打电话的人要么想要全部,要么不想要。让调用者决定返回多少项或何时结束。
我不会说调用者不能使用 for-each 构造。他可以,只要他想要所有的物品。
集合文档中的某些内容(例如“可能是无限集合”)是合适的,但不是“无限迭代器”。
This may be semantics, but the iterator should diligently return the next item without regard to when it will end. Ending is a side effect for a collection, though it may seem like a common side effect.
Still, what's the difference between infinite, and a collection with 10 trillion items? The caller either wants them all, or he doesn't. Let the caller decide how many items to return or when to end.
I wouldn't say the caller couldn't use the for-each construct. He could, as long as he wants all the items.
Something in the documentation for the collection like "may be an infinite collection" would be appropriate, but not "infinite iterator".
这是完全合法的使用 - 只要有适当的记录。
使用 CyclicIterator 名称是一个好主意,因为它推断如果没有正确定义循环退出情况,迭代器上的循环很可能是无限的。
It is a perfectly legitimate use - as long as it is properly documented.
Using the name
CyclicIterator
is a good idea, as it infers that looping on the iterator might well possibly be infinite if the loop exit case is not properly defined.当您创建无限数据时,无限迭代器非常有用,例如线性循环序列,如 斐波那契序列 。
所以使用这样的完全没问题。
An infinite iterator is very useful when you create infinite data, e.g. linear recurrent sequences like the Fibonacci sequence.
So it is totally fine to use such.
是的。您只需要一个不同的标准来确定何时停止迭代。
无限列表的概念在 Haskell 等惰性求值语言中非常常见,并导致完全不同的程序设计。
Yes. You just need a different criteria for when to stop iterating.
The concept of infinite lists is very common in lazy evaluation languages like Haskell and leads to completely different program designs.
实际上,无论 JavaDoc API 怎么说,
Iterator
来自Iterable
,而不是Collection
。后者扩展了前者,这就是为什么它也可以生成Iterator
,但Iterable
不成为Collection
是完全合理的。事实上,甚至有一些 Java 类实现了Iterable
,但没有实现Collection
。现在,至于期望......当然,如果没有明确表明情况确实如此,则不得提供无限迭代器。但有时,期望可能具有欺骗性。例如,人们可能期望
InputStream
总是结束,但这实际上并不是它的要求。同样,返回从此类流读取的行的迭代器可能永远不会结束。Actually, an
Iterator
comes from anIterable
, not aCollection
, whatever the JavaDoc API says. The latter extends the former, and that's why it can generate anIterator
as well, but it's perfectly reasonably for anIterable
not to be aCollection
. In fact, there are even a few Java classes which implementIterable
but notCollection
.Now, as for expectations... of course an infinite iterator must not be made available without a clear indication that this happens to be the case. But, sometimes, expectations can be deceiving. For instance, one might expect an
InputStream
to always come to an end, but that really isn't a requirement of it. Likewise, anIterator
returning lines read from such a stream might never end.考虑一下您是否有生成器函数。正如您所注意到的,迭代器可能是生成器的合理接口,也可能不是。
另一方面,Python 的生成器会终止。
Consider whether you have a generator function. An iterator may be a reasonable interface to a generator, or it might not, as you've noticed.
On the other hand, Python has generators which do terminate.
我可以想象无限迭代器的任意数量的用途。例如,程序迭代由另一台服务器或后台进程发送的状态消息会怎么样。虽然在现实生活中,服务器可能最终会停止,但从程序的角度来看,它可能会继续读取,直到被与迭代器到达末尾无关的东西停止为止。
这肯定是不寻常的,正如其他人所说,应该仔细记录。但我不会宣布这是不可接受的。
I can imagine any number of uses for an infinite iterator. Like, what about a program iterating through status messages being sent by another server or a background process. While in real life the server will presumably stop eventually, from the program's point of view it might just keep reading until it is stopped by something unrelated to the iterator reaching its end.
It would certainly be unusual and, as others have said, should be carefully documented. But I wouldn't declare it unacceptable.
如果您有一个需要迭代器的对象,并且您碰巧给了它一个无限迭代器,那么一切都应该很好。当然,该对象永远不应该要求迭代器停止。 这是潜在的糟糕设计。
想想音乐播放器。您告诉它以“连续播放模式”开始播放曲目,它会播放曲目,直到您告诉它停止为止。也许您可以通过永远随机播放播放列表来实现这种连续播放模式......直到用户按下“停止”。在这种情况下,
MusicPlayer
需要迭代播放列表,可能是Collection
,永远。在这种情况下,您需要一个您构建的CyclicIterator
或一个随机选择下一个轨道且永不耗尽的ShuffleIterator
。到目前为止,一切都很好。MusicPlayer.play(Iterator
不会关心播放列表是否结束。它既可以与轨道列表上的迭代器一起使用(播放到最后,然后停止),也可以与轨道列表上的 ShuffleIteratorIf you have an object that needs an iterator, and you happen to give it an infinite iterator, then all should be well. Of course, that object should never require the iterator to stop. And that is the potentially bad design.
Think of a music player. You tell it to start playing tracks in "continuous play mode" and it plays tracks until you tell it to stop. Maybe you implement this continuous play mode by shuffling a playlist forever... until the user presses "stop". In this case,
MusicPlayer
needs to iterator over a playlist, that might beCollection<Track>
, forever. In this case, you'd want either aCyclicIterator<Track>
as you've built or aShuffleIterator<Track>
that randomly picks the next track and never runs out. So far, so good.MusicPlayer.play(Iterator<Track> playlist)
wouldn't care whether the play list ends or not. It works both with an iterator over a list of tracks (play to the end, then stop) and with aShuffleIterator<Track>
over a list of tracks (pick a random track forever). All still good.What if someone tried to write
MusicPlayer.playContinuously()
--which expects to play tracks "forever" (until someone presses "stop"), but accepts anIterator<Track>
as the parameter? That would be the design problem. Clearly,playContinuously()
needs an infiniteIterator
, and if it accepts a plainIterator
as its parameter, then that would be bad design. It would, for example, need to checkhasNext()
and handle the case where that returnsfalse
, even though it should never happen. Bad.So implement all the infinite iterators you want, as long as their clients don't depend on those iterators ending. As if your client depends on the iterator going on forever, then don't give it a plain
Iterator
.This should be obvious, but it doesn't seem to be.