tl; др Iterables
- это Traversables
, которые могут производить состояния Iterators
Во-первых, знайте, что Iterable
является вычитанием Traversable
.
Второй,
Traversable
требует реализации метода foreach
, который используется всем остальным.
Iterable
требует реализации метода iterator
, который используется всем остальным.
Например, реализация find
для Traversable
использует foreach
(через a для понимания) и выдает исключение BreakControl
для остановки итерации, когда найден удовлетворительный элемент.
trait TravserableLike {
def find(p: A => Boolean): Option[A] = {
var result: Option[A] = None
breakable {
for (x <- this)
if (p(x)) { result = Some(x); break }
}
result
}
}
Напротив, вычитание Iterable
переопределяет эту реализацию и вызывает find
для Iterator
, который просто прекращает итерацию, когда элемент найден:
trait Iterable {
override /*TraversableLike*/ def find(p: A => Boolean): Option[A] =
iterator.find(p)
}
trait Iterator {
def find(p: A => Boolean): Option[A] = {
var res: Option[A] = None
while (res.isEmpty && hasNext) {
val e = next()
if (p(e)) res = Some(e)
}
res
}
}
Было бы неплохо не генерировать исключения для Traversable
итерации, но это единственный способ частично итерировать при использовании только foreach
.
С одной точки зрения, Iterable
является более требовательной / мощной чертой, поскольку вы можете легко реализовать foreach
, используя iterator
, но вы не можете реально реализовать iterator
, используя foreach
.
В итоге, Iterable
предоставляет способ приостановить, возобновить или остановить итерацию с помощью Iterator
с сохранением состояния. С Traversable
это все или ничего (без исключений для управления потоком).
В большинстве случаев это не имеет значения, и вам нужен более общий интерфейс. Но если вам когда-нибудь понадобится более настраиваемый контроль над итерацией, вам понадобится Iterator
, который вы можете получить из Iterable
.