Iterable과 Sequence는 아래와 같이 형태가 비슷하나, usage에 있어 차이가 있다.

interface Iterable<out T> {
		operator fun iterator(): Iterator<T>
}

interface Sequence<out T> {
		operator fun iterator(): Iterator<t>
}

Sequence는 Intermediate function이 연산을 하지 않고, new operation으로 decorate한 새로운 Sequence 객체를 반환한다. 이러한 연산은 toList, count 와 같은 terminal operation에서 한 번에 evaluate 된다.

반면 Iterable은 매 step마다 연산을 적용한 후 List와 같은 collection을 반환한다.

public inline fun <T> Iterable<T>.filter(
		predicate: (T) -> Boolean
): List<T> {
		return filterTo(ArrayList<T>(), predicate)
}

public fun <T> Sequence<T>.filter(
		predicate: (T) -> Boolean
): Sequence<T> {
		return FilteringSequence(this, true, predicate)
}

Sequence의 연산은 원소마다 모든 연산을 적용한 후, 다음 원소에 대해 적용하는데, 이것을 element-by-element 혹은 lazy order라고 부른다.

sequenceOf(1, 2, 3)
    .filter { print("F$it, "); it % 2 == 1 }
    .map { print("M$it, "); it * 2 }
    .forEach { print("E$it, ") }
}
// Prints: F1, M1, E2, F2, F3, M3, E6,

Iterable 연산은 모든 원소에 대해 단계 별로 모든 연산을 적용하는데, 이것을 step-by-step 혹은 eager order 라고 부른다.

listOf(1, 2, 3)
    .filter { print("F$it, "); it % 2 == 1 }
    .map { print("M$it, "); it * 2 }
    .forEach { print("E$it, ") }
}
// Prints: F1, F2, F3, M1, M3, E2, E6,

Sequence의 특징

natrual order of operation

classic loops와 condition을 사용해서 구현한다면 아래와 같이 되는데, 결과는 sequence processing과 동일하다.

for (e in listOf(1,2,3)) {
    print("F$e, ")
    if (e % 2 == 1) {
        print("M$e, ")
        val mapped = e * 2
        print("E$mapped, ")
    }
}
// Prints: F1, M1, E2, F2, F3, M3, E6,

최소한의 연산

collection에 연산을 적용해서 필요로 하는 값이 단 1개라고 했을 때, Iterable을 사용하면, 모든 원소에 대해 매 단계의 연산을 적용하지만, Sequence는 원소마다 모든 연산을 순차적으로 적용하기 때문에 연산 횟수가 더 적다.

// Iterable
(1..10)
    .filter { print("F$it, "); it % 2 == 1 }
    .map { print("M$it, "); it * 2 }
    .find { it > 5 }
}
// Prints: F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, M1, M3, M5, M7, M9,

// Sequence
(1..10).asSequence()
    .filter { print("F$it, "); it % 2 == 1 }
    .map { print("M$it, "); it * 2 }
    .find { it > 5 }

// Prints: F1, M1, F2, F3, M3,