Файл: Одерски Мартин, Спун Лекс, Веннерс Билл, Соммерс ФрэнкО41 Scala. Профессиональное программирование. 5е изд спб. Питер, 2022. 608 с. ил. Серия Библиотека программиста.pdf
ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 09.12.2023
Просмотров: 768
Скачиваний: 11
ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.
СОДЕРЖАНИЕ
565
Как видите, после вызова map итератор it не перешел в конец, а вот итератор, полученный в результате вызова it.map
, можно перебрать до конца.
Еще один пример — метод dropWhile
, который может использоваться для поиска первого элемента итератора, имеющего определенное свойство. На
пример, для поиска в ранее показанном итераторе первого слова, в котором как минимум два символа, можно задействовать следующий код:
val it = Iterator("a", "number", "of", "words")
val dit = it.dropWhile(_.length < 2)
dit.next() // number it.next() // of
Еще раз обратите внимание на то, что it был изменен вызовом dropWhile
: теперь it указывает на второе слово "number"
в списке. Фактически it и ре
зультат res
, полученный при выполнении dropWhile
, вернут одинаковую последовательность элементов.
Есть только одна стандартная операция, duplicate
, позволяющая повторно использовать один и тот же итератор:
val (it1, it2) = it.duplicate
Вызов метода duplicate возвращает два итератора, каждый из которых воз
вращает точно такие же элементы, что и итератор it
. Оба итератора работают независимо друг от друга, продвижение одного из них совершенно не влияет на состояние другого. В отличие от этого исходный итератор it продвигается при вызове duplicate в самый конец и становится непригодным для даль
нейшего использования.
В целом итераторы ведут себя как коллекции, если вы никогда не обра-
щаетесь к итератору еще раз после вызова метода на нем. Библиотеки коллекций Scala делают это явным с помощью абстракции под названием
IterableOnce
, которая является обобщающим супертрейтом для
Iterable и
Iterator
. Судя по названию, объекты типа
IterableOnce допускают обход своих элементов хотя бы однократно, но состояние таких объектов после обхода не определено. Если объект типа
IterableOnce фактически относится к типу
Iterator
, то после обхода будет указывать на конец коллекции, если же относится к типу
Iterable
, то не изменит своего состояния. Чаще всего
IterableOnce используется как тип аргумента для методов, который могут получать в качестве аргумента
Iterator или
Iterable
. Примером может по
служить метод добавления
++
в трейте
Iterable
. Он получает параметр типа
IterableOnce
, позволяя добавлять элементы, получаемые как из коллекции типа
Iterator
, так и из
Iterable
566 Глава 24 • Углубленное изучение коллекций
Все операции над итераторами сведены в табл. 24.14.
Таблица 24.14. Операции в трейте Iterator
Что
Что делает
Абстрактные методы
it.next()
Возвращает следующий элемент в итераторе и продвига
ется за него it.hasNext
Возвращает true
, если может вернуть еще элемент
Вариации
it.buffered
Буферизованный итератор, возвращающий все элемен
ты it it.grouped(size)
Итератор, выдающий элементы, возвращаемые it, блока
ми фиксированного размера it.sliding(size)
Итератор, выдающий элементы, возвращаемые it
, в виде скользящего по коллекции окна фиксированного раз
мера
Копирование
it.copyToArray(arr, s, l)
Копирует не более одного элемента, возвращенного it
, в массив arr
, начиная с индекса s
. Последние два аргу
мента являются необязательными
Дублирование
it.duplicate
Пара итераторов, каждый из которых независимо от другого возвращает все элементы it
Добавления
it ++ jt
Итератор, выдающий все элементы, возвращаемые итератором it
, а затем все элементы, возвращаемые итератором jt it.padTo(len,x)
Итератор, выдающий все элементы it
, а затем копии x
до тех пор, пока не будет полностью достигнута дли
на len
Отображения
it.map(f)
Итератор, получаемый в результате применения функ
ции к каждому элементу, возвращаемому it it.flatMap(f)
Итератор, получаемый в результате применения воз
вращающей итератор функции f
к каждому элементу it и добавления результатов
24 .14 . Итераторы 567
Что
Что делает
it.collect(f)
Итератор, получаемый в результате использования ча
стично примененной функции f
к каждому элементу it
, для которого она определена, и сбора результатов
Преобразования
it.toArray
Собирает возвращаемые it элементы в массив it.toList
Собирает возвращаемые it элементы в список it.toIterable
Собирает возвращаемые it элементы в коллекцию
Iterable it.toSeq
Собирает возвращаемые it элементы в последователь
ность it.toIndexedSeq
Собирает возвращаемые it элементы в индексирован
ную последовательность it.toSet
Собирает возвращаемые it элементы в множество it.toMap
Собирает возвращаемые it пары «ключ — значение» в отображение it to SortedSet
Обобщенная операция преобразования, принимающая в качестве параметра фабрику коллекций
Информация о размере
it.isEmpty
Проверяет, пуст ли итератор (противоположность hasNext
)
it.nonEmpty
Проверяет, содержит ли итератор элементы (псевдоним метода hasNext
)
it.size
Количество элементов, возвращенных it
(после этой операции it окажется в самом конце!)
it.length
То же самое, что и it.size it.knownSize
Количество элементов, если его можно узнать без изме
нения состояния итератора; в противном случае –1
Индексированный поиск с извлечением элемента
it.find(p)
Option
значение, содержащее первый возвращаемый it элемент, удовлетворяющий условию p
, или
None при отсутствии таких элементов (итератор продвигается за найденный элемент или, если не найден ни один эле
мент, оказывается в конечной позиции)
it.indexOf(x)
Индекс первого возвращенного it элемента, равного x
(итератор продвигается за найденный элемент)
568 Глава 24 • Углубленное изучение коллекций
Что
Что делает
it.indexWhere(p)
Индекс первого возвращенного it элемента, удовлетво
ряющего условию p
(итератор продвигается за найден
ный элемент)
Подытераторы
it.take(n)
Итератор, выдающий первые n
элементов итератора it
(
it продвинется за позицию n
го элемента или же оказывается в конечной позиции, если в it содержится меньше n
элементов)
it.drop(n)
Итератор, начинающий выборку с элемента it с позиции
(
n
+ 1) (
it продвинется до этой же позиции)
it.slice(m, n)
Итератор, выдающий блок элементов, возвращенный из it
, начинающийся с m
го элемента и заканчивающий
ся перед n
м элементом it.takeWhile(p)
Итератор, выдающий элементы из it
, пока условие p
вычисляется в true it.dropWhile(p)
Итератор, пропускающий элементы из it
, пока усло
вие p
вычисляется в true
, и выдающий остаток it.filter(p)
Итератор, выдающий все элементы из it
, удовлетворяю
щие условию p
it.withFilter(p)
То же самое, что и it filter p
. Эта операция необходима для использования итераторов в выражениях for it.filterNot(p)
Итератор, выдающий все элементы из it
, не удовлетво
ряющие условию p
it.distinct
Итератор, выдающий все элементы из it
, не включая дубликаты
Деление
it.partition(p)
Разбивает it на два итератора, один из которых возвра
щает из it все элементы, удовлетворяющие условию p
, а другой — все элементы, не удовлетворяющие этому условию
Состояния элементов
it.forall(p)
Булево значение, показывающее, соблюдается ли усло
вие p
для всех элементов, возвращаемых it it.exists(p)
Булево значение, показывающее, соблюдается ли усло
вие p
для какоголибо из элементов, возвращаемых it
Таблица 24.14 (продолжение)
24 .14 . Итераторы 569
Что
Что делает
it.count(p)
Количество элементов в it
, удовлетворяющих условию p
Свертки
it.foldLeft(z)(op)
Применяет бинарную операцию op к соседним эле
ментам, возвращаемым it
, проходя коллекцию слева направо и начиная с z
it.foldRight(z)(op)
Применяет бинарную операцию op к соседним эле
ментам, возвращаемым it
, проходя коллекцию справа налево и начиная с z
it.reduceLeft(op)
Применяет бинарную операцию op к соседним элемен
там, возвращаемым непустым итератором it
, проходя коллекцию слева направо it.reduceRight(op)
Применяет бинарную операцию op к соседним элемен
тами, возвращаемым непустым итератором it
, проходя коллекцию справа налево
Специализированные свертки
it.sum
Сумма значений числовых элементов, выдаваемых итератором it it.product
Произведение значений числовых элементов, выдавае
мых итератором it it.min
Минимальное значение упорядочиваемых элементов, выдаваемых итератором it it.max
Максимальное значение упорядочиваемых элементов, выдаваемых итератором it
Слияния
it zip jt
Итератор пар соответствующих элементов, выдаваемых итераторами it и jt it.zipAll(jt, x, y)
Итератор пар соответствующих элементов, выдаваемых итераторами it и jt
, причем тот итератор, что короче, расширяется, чтобы соответствовать более длинному итератору путем добавления элементов x
или y
it.zipWithIndex
Итератор пар, состоящих из элементов, возвращае
мых it
, и их индексов
Обновление
it.patch(i, jt, r)
Итератор, получаемый из it путем замены r
элементов, начиная с позиции i
, итератором вставки jt
570 Глава 24 • Углубленное изучение коллекций
Что
Что делает
Сравнение
it.sameElements(jt)
Проверяет, не возвращают ли итераторы it и
jt одни и те же элементы в одном и том же порядке. Обрати
те внимание: после этой операции следует отбросить как it
, так и jt
Строки
it.addString(b, start, sep, end)
Добавляет строку к
StringBuilder b
, которая показывает все элементы, возвращаемые it
, между разделителями sep и заключена в строковые значения start и end
. Аргу
менты start
, sep и end являются необязательными it.mkString(start, sep, end)
Преобразует итератор в строку, показывающую все элементы, возвращаемые it
, между разделителями sep
, и заключенную в строковые значения start и end
. Аргу
менты start
, sep и end являются необязательными
Буферизованные итераторы
Порой бывает необходим итератор, способный заглянуть вперед, позволяя проинспектировать следующий возвращаемый элемент, не продвигаясь за него. Рассмотрим, к примеру, задачу пропуска начальных пустых строк ите
ратора, который возвращает последовательность строк. Возможно, возникнет соблазн создать нечто похожее на метод, показанный ниже:
// Этот код работать не будет def skipEmptyWordsNOT(it: Iterator[String]) =
while it.next().isEmpty do {}
Но, присмотревшись, можно понять, что этот код неработоспособен: разуме
ется, он будет пропускать начальные пустые строки, но также продвинет it за первую непустую строку!
Решить эту задачу можно с помощью буферизованного итератора, эк
земпляра трейта
BufferedIterator
, который является подтрейтом трейта
Iterator и предоставляет еще один метод по имени head
. Вызов head в отношении буферизованного итератора приведет к возвращению его первого элемента, но без продвижения итератора. При использовании бу
феризованного итератора код для пропуска пустых слов может выглядеть следующим образом:
Таблица 24.14 (окончание)
24 .15 . Создание коллекций с нуля 571
def skipEmptyWords(it: BufferedIterator[String]) =
while it.head.isEmpty do it.next()
Каждый итератор может быть преобразован в буферизованный итератор путем вызова своего метода buffered
. Пример его использования выглядит так:
val it = Iterator(1, 2, 3, 4)
val bit = it.buffered bit.head // 1
bit.next() // 1
bit.next() // 2
Следует заметить, что вызов head в отношении буферизованного итератора bit не приводит к изменению позиции итератора. Поэтому последующий вызов, bit.next()
, возвращает то же самое значение, что и bit.head
24 .15 . Создание коллекций с нуля
Вам уже попадался синтаксис наподобие
List(1,
2,
3)
, который создает список из трех целых чисел, и
Map('A'
–>
1,
'C'
–>
2)
, который создает ото
бражение с двумя привязками. Фактически это универсальная возможность коллекций Scala. Можно взять любое имя коллекции и указать после него в круглых скобках список элементов. В результате получится новая коллек
ция с заданными элементами. Ниже представлены еще примеры:
Iterable() // пустая коллекция
List() // пустой список
List(1.0, 2.0) // список с элементами 1.0, 2.0
Vector(1.0, 2.0) // вектор с элементами 1.0, 2.0
Iterator(1, 2, 3) // итератор, возвращающий три целых числа
Set(dog, cat, bird) // множество из трех животных
HashSet(dog, cat, bird) // хеш-множество из тех же животных
Map('a' –> 7, 'b' –> 0) // отображение символов на целые числа
«Скрытно» каждая из показанных ранее строк является вызовом метода apply определенного объекта. Например, третья из этих строк раскрывается в следующий код:
List.apply(1.0, 2.0)
Здесь показан вызов метода apply
, принадлежащего объектукомпаньону класса
List
. Этот метод получает произвольное число аргументов и создает из них список. Каждый класс коллекций в библиотеке Scala располагает
572 Глава 24 • Углубленное изучение коллекций объектомкомпаньоном с таким же методом apply
. И неважно, представлена конкретная реализация классом коллекции, как в случае с
List
,
LazyList или
Vector
, или же трейтом, как в случае с
Seq
,
Set или
Iterable
. В последнем случае вызов apply приведет к созданию некой исходной реализации трейта.
Рассмотрим ряд примеров:
scala> List(1, 2, 3)
val res17: List[Int] = List(1, 2, 3)
scala> Iterable(1, 2, 3)
val res18: Iterable[Int] = List(1, 2, 3)
scala> mutable.Iterable(1, 2, 3)
val res19: scala.collection.mutable.Iterable[Int] =
ArrayBuffer(1, 2, 3)
Помимо apply
, в каждом объектекомпаньоне определен и элемент empty
, возвращающий пустую коллекцию. Поэтому вместо
List()
можно вос
пользоваться кодом
List.empty
, вместо
Map()
задействовать код
Map.empty и т. д.
Кроме того, потомки трейтов
Seq и
Set в своих объектахкомпаньонах пре
доставляют другие факторные операции, которые сведены в табл. 24.15.
Вкратце это:
z z
concat
, которая конкатенирует произвольное количество коллекций;
z z
fill и tabulate
, которые создают одно или многоразмерные коллекции заданной размерности, инициализированные какимлибо выражением или функцией составления таблицы;
z z
range
, которая создает коллекции целых чисел с какойлибо постоянной длиной шага;
z z
iterate и unfold
, которые создают последовательность, получающуюся из многократного применения функции к начальному элементу или со
стоянию.
Таблица 24.15. Фабричные методы для трейтов Seq и Set
Что
Что делает
C.empty
Пустая коллекция
C(x, y, z)
Коллекция, состоящая из элементов x
, y
и z
C.concat(xs, ys, zs)
Коллекция, получаемая конкатенацией элементов коллекций xs
, ys и zs
24 .16 . Преобразования между коллекциями Java и Scala 573
Что
Что делает
C.fill(n)(e)
Коллекция длиной n
, где каждый элемент вычисля
ется выражением e
C.fill(m, n)(e)
Коллекция коллекций размерностью m
× n
, где каждый элемент вычисляется выражением e
(суще
ствует также в более высоких измерениях)
C.tabulate(n)(f)
Коллекция длиной n
, где элемент по каждому ин
дексу i
вычисляется путем вызова f(i)
C.tabulate(m, n)(f)
Коллекция коллекций размерностью m
× n
, где эле
мент по каждому индексу (
i,
j
) вычисляется путем вызова f(i,
j)
(существует также в более высоких измерениях)
C.range(start, end)
Коллекция целых чисел start end
—
1
C.range(start, end, step)
Коллекция целых чисел, начинающаяся со start и наращиваемая с шагом step до значения end
, ис
ключая само это значение
C.iterate(x, n)(f)
Коллекция длиной n
с элементами x,
f(x),
f(f(x)),
C.unfold(init)(f)
Коллекция, которая использует функцию f
для вы
числения своего следующего элемента и состояния, начиная с состояния init
24 .16 . Преобразования между коллекциями
Java и Scala
Как и в Scala, в Java есть богатая библиотека коллекций. Обе библиотеки имеют много общего. Например, и в той и в другой есть такие категории, как итераторы, итерируемые коллекции, множества, отображения и по
следовательности. Но есть и важные различия. В частности, в библиотеках
Scala уделяется намного больше внимания неизменяемым коллекциям и предоставляется куда больше операций, выполняющих преобразование коллекции в новую коллекцию.
Иногда может понадобиться выполнить преобразование из одной среды в другую. Например, нужно обратиться к уже существующей Javaколлекции, как если бы это была Scalaколлекция. Или же следует передать одну из коллекций Scala методу Java, который ожидает получения Javaаналога.
Сделать это не составит никакого труда, так как Scala предлагает в объекте
574 Глава 24 • Углубленное изучение коллекций
CollectionConverters удобные преобразования между всеми основными типами коллекций. В частности, двунаправленные преобразования имеются между следующими типами:
Iterator
⇔ java.util.Iterator
Iterator
⇔ java.util.Enumeration
Iterable
⇔ java.lang.Iterable
Iterable
⇔ java.util.Collection mutable.Buffer
⇔ java.util.List mutable.Set
⇔ java.util.Set mutable.Map
⇔ java.util.Map
Чтобы включить эти преобразования, нужно просто импортировать их:
scala> import jdk.CollectionConverters.*
Это делает возможным преобразования между соответствующими коллек
циями Scala и Java с помощью методов расширения asScala и asJava
:
scala> import collection.mutable.*
scala> val jul: java.util.List[Int] = ArrayBuffer(1, 2, 3).asJava val jul: java.util.List[Int] = [1, 2, 3]
scala> val buf: Seq[Int] = jul.asScala val buf: scala.collection.mutable.Seq[Int] = ArrayBuffer(1, 2, 3)
scala> val m: java.util.Map[String, Int] =
HashMap("abc" –> 1, "hello" –> 2).asJava m: java.util.Map[String,Int] = {hello=2, abc=1}
Внутренний механизм этих преобразований работает за счет создания объ
ектаобертки, пересылающего все операции базовому объекту коллекции.
Поэтому коллекции при преобразовании между Java и Scala никогда не копируются. Интересной особенностью является то, что при круговом пре
образовании из, скажем, Javaтипа в соответствующий Scalaтип и обратно, в тот же Javaтип, будет получен точно такой же объект коллекции, который имелся в самом начале.
Есть также ряд других востребованных Scalaколлекций, которые могут быть преобразованы в Javaтипы, но для которых нет соответствующих преобра
зований в обратном направлении. К ним относятся:
Seq
⇒ java.util.List mutable.Seq
⇒ java.util.List
Set
⇒ java.util.Set
Map
⇒ java.util.Map
Как видите, после вызова map итератор it не перешел в конец, а вот итератор, полученный в результате вызова it.map
, можно перебрать до конца.
Еще один пример — метод dropWhile
, который может использоваться для поиска первого элемента итератора, имеющего определенное свойство. На
пример, для поиска в ранее показанном итераторе первого слова, в котором как минимум два символа, можно задействовать следующий код:
val it = Iterator("a", "number", "of", "words")
val dit = it.dropWhile(_.length < 2)
dit.next() // number it.next() // of
Еще раз обратите внимание на то, что it был изменен вызовом dropWhile
: теперь it указывает на второе слово "number"
в списке. Фактически it и ре
зультат res
, полученный при выполнении dropWhile
, вернут одинаковую последовательность элементов.
Есть только одна стандартная операция, duplicate
, позволяющая повторно использовать один и тот же итератор:
val (it1, it2) = it.duplicate
Вызов метода duplicate возвращает два итератора, каждый из которых воз
вращает точно такие же элементы, что и итератор it
. Оба итератора работают независимо друг от друга, продвижение одного из них совершенно не влияет на состояние другого. В отличие от этого исходный итератор it продвигается при вызове duplicate в самый конец и становится непригодным для даль
нейшего использования.
В целом итераторы ведут себя как коллекции, если вы никогда не обра-
щаетесь к итератору еще раз после вызова метода на нем. Библиотеки коллекций Scala делают это явным с помощью абстракции под названием
IterableOnce
, которая является обобщающим супертрейтом для
Iterable и
Iterator
. Судя по названию, объекты типа
IterableOnce допускают обход своих элементов хотя бы однократно, но состояние таких объектов после обхода не определено. Если объект типа
IterableOnce фактически относится к типу
Iterator
, то после обхода будет указывать на конец коллекции, если же относится к типу
Iterable
, то не изменит своего состояния. Чаще всего
IterableOnce используется как тип аргумента для методов, который могут получать в качестве аргумента
Iterator или
Iterable
. Примером может по
служить метод добавления
++
в трейте
Iterable
. Он получает параметр типа
IterableOnce
, позволяя добавлять элементы, получаемые как из коллекции типа
Iterator
, так и из
Iterable
566 Глава 24 • Углубленное изучение коллекций
Все операции над итераторами сведены в табл. 24.14.
Таблица 24.14. Операции в трейте Iterator
Что
Что делает
Абстрактные методы
it.next()
Возвращает следующий элемент в итераторе и продвига
ется за него it.hasNext
Возвращает true
, если может вернуть еще элемент
Вариации
it.buffered
Буферизованный итератор, возвращающий все элемен
ты it it.grouped(size)
Итератор, выдающий элементы, возвращаемые it, блока
ми фиксированного размера it.sliding(size)
Итератор, выдающий элементы, возвращаемые it
, в виде скользящего по коллекции окна фиксированного раз
мера
Копирование
it.copyToArray(arr, s, l)
Копирует не более одного элемента, возвращенного it
, в массив arr
, начиная с индекса s
. Последние два аргу
мента являются необязательными
Дублирование
it.duplicate
Пара итераторов, каждый из которых независимо от другого возвращает все элементы it
Добавления
it ++ jt
Итератор, выдающий все элементы, возвращаемые итератором it
, а затем все элементы, возвращаемые итератором jt it.padTo(len,x)
Итератор, выдающий все элементы it
, а затем копии x
до тех пор, пока не будет полностью достигнута дли
на len
Отображения
it.map(f)
Итератор, получаемый в результате применения функ
ции к каждому элементу, возвращаемому it it.flatMap(f)
Итератор, получаемый в результате применения воз
вращающей итератор функции f
к каждому элементу it и добавления результатов
24 .14 . Итераторы 567
Что
Что делает
it.collect(f)
Итератор, получаемый в результате использования ча
стично примененной функции f
к каждому элементу it
, для которого она определена, и сбора результатов
Преобразования
it.toArray
Собирает возвращаемые it элементы в массив it.toList
Собирает возвращаемые it элементы в список it.toIterable
Собирает возвращаемые it элементы в коллекцию
Iterable it.toSeq
Собирает возвращаемые it элементы в последователь
ность it.toIndexedSeq
Собирает возвращаемые it элементы в индексирован
ную последовательность it.toSet
Собирает возвращаемые it элементы в множество it.toMap
Собирает возвращаемые it пары «ключ — значение» в отображение it to SortedSet
Обобщенная операция преобразования, принимающая в качестве параметра фабрику коллекций
Информация о размере
it.isEmpty
Проверяет, пуст ли итератор (противоположность hasNext
)
it.nonEmpty
Проверяет, содержит ли итератор элементы (псевдоним метода hasNext
)
it.size
Количество элементов, возвращенных it
(после этой операции it окажется в самом конце!)
it.length
То же самое, что и it.size it.knownSize
Количество элементов, если его можно узнать без изме
нения состояния итератора; в противном случае –1
Индексированный поиск с извлечением элемента
it.find(p)
Option
значение, содержащее первый возвращаемый it элемент, удовлетворяющий условию p
, или
None при отсутствии таких элементов (итератор продвигается за найденный элемент или, если не найден ни один эле
мент, оказывается в конечной позиции)
it.indexOf(x)
Индекс первого возвращенного it элемента, равного x
(итератор продвигается за найденный элемент)
568 Глава 24 • Углубленное изучение коллекций
Что
Что делает
it.indexWhere(p)
Индекс первого возвращенного it элемента, удовлетво
ряющего условию p
(итератор продвигается за найден
ный элемент)
Подытераторы
it.take(n)
Итератор, выдающий первые n
элементов итератора it
(
it продвинется за позицию n
го элемента или же оказывается в конечной позиции, если в it содержится меньше n
элементов)
it.drop(n)
Итератор, начинающий выборку с элемента it с позиции
(
n
+ 1) (
it продвинется до этой же позиции)
it.slice(m, n)
Итератор, выдающий блок элементов, возвращенный из it
, начинающийся с m
го элемента и заканчивающий
ся перед n
м элементом it.takeWhile(p)
Итератор, выдающий элементы из it
, пока условие p
вычисляется в true it.dropWhile(p)
Итератор, пропускающий элементы из it
, пока усло
вие p
вычисляется в true
, и выдающий остаток it.filter(p)
Итератор, выдающий все элементы из it
, удовлетворяю
щие условию p
it.withFilter(p)
То же самое, что и it filter p
. Эта операция необходима для использования итераторов в выражениях for it.filterNot(p)
Итератор, выдающий все элементы из it
, не удовлетво
ряющие условию p
it.distinct
Итератор, выдающий все элементы из it
, не включая дубликаты
Деление
it.partition(p)
Разбивает it на два итератора, один из которых возвра
щает из it все элементы, удовлетворяющие условию p
, а другой — все элементы, не удовлетворяющие этому условию
Состояния элементов
it.forall(p)
Булево значение, показывающее, соблюдается ли усло
вие p
для всех элементов, возвращаемых it it.exists(p)
Булево значение, показывающее, соблюдается ли усло
вие p
для какоголибо из элементов, возвращаемых it
Таблица 24.14 (продолжение)
24 .14 . Итераторы 569
Что
Что делает
it.count(p)
Количество элементов в it
, удовлетворяющих условию p
Свертки
it.foldLeft(z)(op)
Применяет бинарную операцию op к соседним эле
ментам, возвращаемым it
, проходя коллекцию слева направо и начиная с z
it.foldRight(z)(op)
Применяет бинарную операцию op к соседним эле
ментам, возвращаемым it
, проходя коллекцию справа налево и начиная с z
it.reduceLeft(op)
Применяет бинарную операцию op к соседним элемен
там, возвращаемым непустым итератором it
, проходя коллекцию слева направо it.reduceRight(op)
Применяет бинарную операцию op к соседним элемен
тами, возвращаемым непустым итератором it
, проходя коллекцию справа налево
Специализированные свертки
it.sum
Сумма значений числовых элементов, выдаваемых итератором it it.product
Произведение значений числовых элементов, выдавае
мых итератором it it.min
Минимальное значение упорядочиваемых элементов, выдаваемых итератором it it.max
Максимальное значение упорядочиваемых элементов, выдаваемых итератором it
Слияния
it zip jt
Итератор пар соответствующих элементов, выдаваемых итераторами it и jt it.zipAll(jt, x, y)
Итератор пар соответствующих элементов, выдаваемых итераторами it и jt
, причем тот итератор, что короче, расширяется, чтобы соответствовать более длинному итератору путем добавления элементов x
или y
it.zipWithIndex
Итератор пар, состоящих из элементов, возвращае
мых it
, и их индексов
Обновление
it.patch(i, jt, r)
Итератор, получаемый из it путем замены r
элементов, начиная с позиции i
, итератором вставки jt
570 Глава 24 • Углубленное изучение коллекций
Что
Что делает
Сравнение
it.sameElements(jt)
Проверяет, не возвращают ли итераторы it и
jt одни и те же элементы в одном и том же порядке. Обрати
те внимание: после этой операции следует отбросить как it
, так и jt
Строки
it.addString(b, start, sep, end)
Добавляет строку к
StringBuilder b
, которая показывает все элементы, возвращаемые it
, между разделителями sep и заключена в строковые значения start и end
. Аргу
менты start
, sep и end являются необязательными it.mkString(start, sep, end)
Преобразует итератор в строку, показывающую все элементы, возвращаемые it
, между разделителями sep
, и заключенную в строковые значения start и end
. Аргу
менты start
, sep и end являются необязательными
Буферизованные итераторы
Порой бывает необходим итератор, способный заглянуть вперед, позволяя проинспектировать следующий возвращаемый элемент, не продвигаясь за него. Рассмотрим, к примеру, задачу пропуска начальных пустых строк ите
ратора, который возвращает последовательность строк. Возможно, возникнет соблазн создать нечто похожее на метод, показанный ниже:
// Этот код работать не будет def skipEmptyWordsNOT(it: Iterator[String]) =
while it.next().isEmpty do {}
Но, присмотревшись, можно понять, что этот код неработоспособен: разуме
ется, он будет пропускать начальные пустые строки, но также продвинет it за первую непустую строку!
Решить эту задачу можно с помощью буферизованного итератора, эк
земпляра трейта
BufferedIterator
, который является подтрейтом трейта
Iterator и предоставляет еще один метод по имени head
. Вызов head в отношении буферизованного итератора приведет к возвращению его первого элемента, но без продвижения итератора. При использовании бу
феризованного итератора код для пропуска пустых слов может выглядеть следующим образом:
Таблица 24.14 (окончание)
24 .15 . Создание коллекций с нуля 571
def skipEmptyWords(it: BufferedIterator[String]) =
while it.head.isEmpty do it.next()
Каждый итератор может быть преобразован в буферизованный итератор путем вызова своего метода buffered
. Пример его использования выглядит так:
val it = Iterator(1, 2, 3, 4)
val bit = it.buffered bit.head // 1
bit.next() // 1
bit.next() // 2
Следует заметить, что вызов head в отношении буферизованного итератора bit не приводит к изменению позиции итератора. Поэтому последующий вызов, bit.next()
, возвращает то же самое значение, что и bit.head
24 .15 . Создание коллекций с нуля
Вам уже попадался синтаксис наподобие
List(1,
2,
3)
, который создает список из трех целых чисел, и
Map('A'
–>
1,
'C'
–>
2)
, который создает ото
бражение с двумя привязками. Фактически это универсальная возможность коллекций Scala. Можно взять любое имя коллекции и указать после него в круглых скобках список элементов. В результате получится новая коллек
ция с заданными элементами. Ниже представлены еще примеры:
Iterable() // пустая коллекция
List() // пустой список
List(1.0, 2.0) // список с элементами 1.0, 2.0
Vector(1.0, 2.0) // вектор с элементами 1.0, 2.0
Iterator(1, 2, 3) // итератор, возвращающий три целых числа
Set(dog, cat, bird) // множество из трех животных
HashSet(dog, cat, bird) // хеш-множество из тех же животных
Map('a' –> 7, 'b' –> 0) // отображение символов на целые числа
«Скрытно» каждая из показанных ранее строк является вызовом метода apply определенного объекта. Например, третья из этих строк раскрывается в следующий код:
List.apply(1.0, 2.0)
Здесь показан вызов метода apply
, принадлежащего объектукомпаньону класса
List
. Этот метод получает произвольное число аргументов и создает из них список. Каждый класс коллекций в библиотеке Scala располагает
572 Глава 24 • Углубленное изучение коллекций объектомкомпаньоном с таким же методом apply
. И неважно, представлена конкретная реализация классом коллекции, как в случае с
List
,
LazyList или
Vector
, или же трейтом, как в случае с
Seq
,
Set или
Iterable
. В последнем случае вызов apply приведет к созданию некой исходной реализации трейта.
Рассмотрим ряд примеров:
scala> List(1, 2, 3)
val res17: List[Int] = List(1, 2, 3)
scala> Iterable(1, 2, 3)
val res18: Iterable[Int] = List(1, 2, 3)
scala> mutable.Iterable(1, 2, 3)
val res19: scala.collection.mutable.Iterable[Int] =
ArrayBuffer(1, 2, 3)
Помимо apply
, в каждом объектекомпаньоне определен и элемент empty
, возвращающий пустую коллекцию. Поэтому вместо
List()
можно вос
пользоваться кодом
List.empty
, вместо
Map()
задействовать код
Map.empty и т. д.
Кроме того, потомки трейтов
Seq и
Set в своих объектахкомпаньонах пре
доставляют другие факторные операции, которые сведены в табл. 24.15.
Вкратце это:
z z
concat
, которая конкатенирует произвольное количество коллекций;
z z
fill и tabulate
, которые создают одно или многоразмерные коллекции заданной размерности, инициализированные какимлибо выражением или функцией составления таблицы;
z z
range
, которая создает коллекции целых чисел с какойлибо постоянной длиной шага;
z z
iterate и unfold
, которые создают последовательность, получающуюся из многократного применения функции к начальному элементу или со
стоянию.
Таблица 24.15. Фабричные методы для трейтов Seq и Set
Что
Что делает
C.empty
Пустая коллекция
C(x, y, z)
Коллекция, состоящая из элементов x
, y
и z
C.concat(xs, ys, zs)
Коллекция, получаемая конкатенацией элементов коллекций xs
, ys и zs
24 .16 . Преобразования между коллекциями Java и Scala 573
Что
Что делает
C.fill(n)(e)
Коллекция длиной n
, где каждый элемент вычисля
ется выражением e
C.fill(m, n)(e)
Коллекция коллекций размерностью m
× n
, где каждый элемент вычисляется выражением e
(суще
ствует также в более высоких измерениях)
C.tabulate(n)(f)
Коллекция длиной n
, где элемент по каждому ин
дексу i
вычисляется путем вызова f(i)
C.tabulate(m, n)(f)
Коллекция коллекций размерностью m
× n
, где эле
мент по каждому индексу (
i,
j
) вычисляется путем вызова f(i,
j)
(существует также в более высоких измерениях)
C.range(start, end)
Коллекция целых чисел start end
—
1
C.range(start, end, step)
Коллекция целых чисел, начинающаяся со start и наращиваемая с шагом step до значения end
, ис
ключая само это значение
C.iterate(x, n)(f)
Коллекция длиной n
с элементами x,
f(x),
f(f(x)),
C.unfold(init)(f)
Коллекция, которая использует функцию f
для вы
числения своего следующего элемента и состояния, начиная с состояния init
24 .16 . Преобразования между коллекциями
Java и Scala
Как и в Scala, в Java есть богатая библиотека коллекций. Обе библиотеки имеют много общего. Например, и в той и в другой есть такие категории, как итераторы, итерируемые коллекции, множества, отображения и по
следовательности. Но есть и важные различия. В частности, в библиотеках
Scala уделяется намного больше внимания неизменяемым коллекциям и предоставляется куда больше операций, выполняющих преобразование коллекции в новую коллекцию.
Иногда может понадобиться выполнить преобразование из одной среды в другую. Например, нужно обратиться к уже существующей Javaколлекции, как если бы это была Scalaколлекция. Или же следует передать одну из коллекций Scala методу Java, который ожидает получения Javaаналога.
Сделать это не составит никакого труда, так как Scala предлагает в объекте
574 Глава 24 • Углубленное изучение коллекций
CollectionConverters удобные преобразования между всеми основными типами коллекций. В частности, двунаправленные преобразования имеются между следующими типами:
Iterator
⇔ java.util.Iterator
Iterator
⇔ java.util.Enumeration
Iterable
⇔ java.lang.Iterable
Iterable
⇔ java.util.Collection mutable.Buffer
⇔ java.util.List mutable.Set
⇔ java.util.Set mutable.Map
⇔ java.util.Map
Чтобы включить эти преобразования, нужно просто импортировать их:
scala> import jdk.CollectionConverters.*
Это делает возможным преобразования между соответствующими коллек
циями Scala и Java с помощью методов расширения asScala и asJava
:
scala> import collection.mutable.*
scala> val jul: java.util.List[Int] = ArrayBuffer(1, 2, 3).asJava val jul: java.util.List[Int] = [1, 2, 3]
scala> val buf: Seq[Int] = jul.asScala val buf: scala.collection.mutable.Seq[Int] = ArrayBuffer(1, 2, 3)
scala> val m: java.util.Map[String, Int] =
HashMap("abc" –> 1, "hello" –> 2).asJava m: java.util.Map[String,Int] = {hello=2, abc=1}
Внутренний механизм этих преобразований работает за счет создания объ
ектаобертки, пересылающего все операции базовому объекту коллекции.
Поэтому коллекции при преобразовании между Java и Scala никогда не копируются. Интересной особенностью является то, что при круговом пре
образовании из, скажем, Javaтипа в соответствующий Scalaтип и обратно, в тот же Javaтип, будет получен точно такой же объект коллекции, который имелся в самом начале.
Есть также ряд других востребованных Scalaколлекций, которые могут быть преобразованы в Javaтипы, но для которых нет соответствующих преобра
зований в обратном направлении. К ним относятся:
Seq
⇒ java.util.List mutable.Seq
⇒ java.util.List
Set
⇒ java.util.Set
Map
⇒ java.util.Map