Конкатенация последовательности Котлина

val seq1 = sequenceOf(1, 2, 3)
val seq2 = sequenceOf(5, 6, 7)
sequenceOf(seq1, seq2).flatten().forEach { ... }

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

Есть ли такая функция?

Ответ 1

Ваш код не копирует элементы последовательности, а sequenceOf(seq1, seq2).flatten() выполняет то, что вам нужно: он генерирует последовательность, которая берет элементы сначала из seq1, а затем, когда seq1 заканчивается, из seq2.

Кроме того, оператор + реализуется именно таким образом, поэтому вы можете просто использовать его:

(seq1 + seq2).forEach { ... }

Источник оператора как ожидалось:

public operator fun <T> Sequence<T>.plus(elements: Sequence<T>): Sequence<T> {
    return sequenceOf(this, elements).flatten()
}

Вы можете взглянуть на реализацию .flatten() в stdlib, которая использует FlatteningSequence, который фактически переключает итераторы исходных последовательностей. Реализация может изменяться с течением времени, но Sequence должна быть максимально ленивой, поэтому вы можете ожидать, что она будет вести себя аналогичным образом.

Пример:

val a = generateSequence(0) { it + 1 }
val b = sequenceOf(1, 2, 3)

(a + b).take(3).forEach { println(it) }

Здесь копирование первой последовательности никогда не будет успешным, так как оно бесконечно, а итерация по (a + b) берет элементы один за другим из a.


Обратите внимание, однако, что .flatten() реализуется по-другому для Iterable, а делает скопируйте элементы. Подробнее о различиях между Iterable и Sequence здесь.