Подтвердить что ты не робот

Какова связь между Iterable и Iterator?

В чем разница между Iterator и Iterable в scala?

Я думал, что Iterable представляет собой набор, который я могу выполнить, и Iterator является "указателем" на один из элементов в итеративном наборе.

Однако Iterator имеет такие функции, как forEach, map, foldLeft. Его можно преобразовать в Iterable через toIterable. И, например, scala.io.Source.getLines возвращает Iterator, а не Iterable.

Но я не могу сделать groupBy на Iterator, и я могу сделать это на Iterable.

Итак, какова связь между этими двумя, Iterator и Iterable?

4b9b3361

Ответ 1

Короче: An Iterator имеет состояние, а Iterable - нет.

Смотрите документы API для обоих.

Iterable:

Базовый признак для повторяющихся коллекций.

Это базовый признак для всех коллекций Scala, которые определяют итератор метод для каждого элемента коллекции. [...] Эта черта реализует метод Iterable foreach путем степпинга через все элементы, использующие итератор.

Iterator:

Итераторы представляют собой структуры данных, которые позволяют выполнять итерацию по последовательности элементы. У них есть метод hasNext для проверки наличия следующего доступный элемент и следующий метод, который возвращает следующий элемент и отбрасывает его из итератора.

Итератор изменен: большинство операций на нем меняет свое состояние. В то время как он часто используется для повторения элементов коллекции, это также могут использоваться без поддержки какой-либо коллекции (см. конструкторы на сопутствующем объекте).

С помощью Iterator вы можете остановить итерацию и продолжить ее позже, если хотите. Если вы попытаетесь сделать это с помощью Iterable, он снова начнется с головы:

scala> val iterable: Iterable[Int] = 1 to 4
iterable: Iterable[Int] = Range(1, 2, 3, 4)

scala> iterable.take(2)
res8: Iterable[Int] = Range(1, 2)

scala> iterable.take(2)
res9: Iterable[Int] = Range(1, 2)

scala> val iterator = iterable.iterator
iterator: Iterator[Int] = non-empty iterator

scala> if (iterator.hasNext) iterator.next
res23: AnyVal = 1

scala> if (iterator.hasNext) iterator.next
res24: AnyVal = 2

scala> if (iterator.hasNext) iterator.next
res25: AnyVal = 3

scala> if (iterator.hasNext) iterator.next
res26: AnyVal = 4

scala> if (iterator.hasNext) iterator.next
res27: AnyVal = ()

Обратите внимание, что я не использовал take на Iterator. Причина этого в том, что это сложно использовать. hasNext и next - это единственные два метода, которые гарантированно работают как ожидается на Iterator. Подробнее см. Scaladoc:

Особо важно отметить, что, если не указано иное, никогда нельзя использовать итератор после вызова метода на нем. Два наиболее важными исключениями являются также единственные абстрактные методы: следующий и hasNext.

Оба эти метода можно назвать любое количество раз, не отменить итератор. Обратите внимание, что даже hasNext может вызвать мутацию - например, при повторении из входного потока, где он будет блокироваться до поток закрывается или становится доступным какой-либо вход.

Рассмотрим этот пример для безопасного и небезопасного использования:

def f[A](it: Iterator[A]) = {
  if (it.hasNext) {            // Safe to reuse "it" after "hasNext"
    it.next                    // Safe to reuse "it" after "next"
    val remainder = it.drop(2) // it is *not* safe to use "it" again after this line!
    remainder.take(2)          // it is *not* safe to use "remainder" after this line!
  } else it
}

Ответ 2

Еще одно объяснение от Мартина Одерского и Лекса Лоун:

Там важная разница между методом foreach на итераторы и один и тот же метод для пересекающихся коллекций: когда вызывается на итератор, foreach оставит итератор на своем конце, когда он сделанный. Таким образом, вызов снова на том же итераторе потерпит неудачу с NoSuchElementException. Напротив, когда вызывается в коллекции, foreach оставляет количество элементов в коллекции неизменным (если переданная функция не добавляет к удалению элементов, но это обескуражен, потому что это может привести к неожиданным результатам).

Источник: http://www.scala-lang.org/docu/files/collections-api/collections_43.html

Заметьте также (благодаря Wei-Ching Lin для этого наконечника) Iterator расширяет черту TraversableOnce, а Iterable не делает.

Ответ 3

Заметьте также (благодаря Wei-Ching Lin для этого наконечника) Iterator расширяет TraversableOnce trait while Iterable не делает.

В scala 2.11 оба Итератора и Итерабеля расширяют TraversableOnce.