Sequences.

Collections process all elements at each step. Filter the entire list, then map the entire result. This can be wasteful for large data or when you only need some elements. Sequences offer a different approach: lazy evaluation. Today we learn when and how to use sequences.

Collection operations are eager. They process everything immediately.

list dot filter dot map dot first. Filter processes all elements. Map processes all results. Then you take the first.

For small collections, this is fine. For large collections, this creates waste.

Imagine a million elements. Filter creates a list of half a million. Map creates another list of half a million. You take one element. Two intermediate lists for one result.

The waste is memory. Creating large intermediate collections costs.

The waste is time. Processing elements you never use.

Sequences are lazy collections.

They do not process elements until needed. They do not create intermediate collections.

sequence dot filter dot map dot first. No intermediate lists. Each element flows through the chain until a result is produced.

With sequences, elements are processed one at a time. Take an element from the source. Apply filter. If it passes, apply map. If you need more, take the next element.

This is lazy evaluation. Defer work until results are required.

From an existing collection: list dot asSequence.

From elements: sequenceOf 1, 2, 3.

From a generator: generateSequence 1 open brace it plus 1 close brace creates 1, 2, 3, forever.

Using sequence builder: sequence open brace yield 1 semicolon yield 2 semicolon yield 3 close brace.

sequenceOf is like listOf but lazy.

generateSequence creates potentially infinite sequences. Be careful: you must eventually stop consuming.

The sequence builder gives fine control. You yield elements one at a time.

Sequence operations are either intermediate or terminal.

Intermediate operations return another sequence. They are lazy. Nothing happens yet. filter, map, take, drop are intermediate.

Terminal operations produce a result. They trigger processing. toList, first, sum, count are terminal.

sequence dot filter dot map does nothing. It defines a pipeline.

sequence dot filter dot map dot toList executes. Elements flow through.

Think of intermediate operations as building a plan. Terminal operations execute the plan.

Collections process horizontally. All elements through filter, then all through map.

Sequences process vertically. One element through the entire pipeline, then the next.

Consider: list dot filter dot map dot take 2.

With a collection: filter all elements, then map all passing elements, then take two.

With a sequence: process first element, if it passes filter and map emit it, process next element. Once take 2 has two results, processing stops.

Sequences stop early. Once take 2 has two results, processing stops.

Use sequences for large collections. The lazy processing avoids intermediate lists.

Use sequences when you need only some elements. first, take n, find benefit from early termination.

Use sequences for chained operations. Multiple filter and map steps multiply intermediate collections in eager mode.

Do not use sequences for small collections. The overhead of lazy evaluation can exceed the savings.

Do not use sequences when you need indexed access. Sequences do not support random access.

The rule of thumb: if you have thousands of elements or more, and you chain multiple operations, consider sequences.

Sequences are not always faster.

For simple operations on small data, collections are faster. The overhead of lazy evaluation hurts.

For complex chains on large data, sequences win. No intermediate collections. Early termination.

Benchmarks vary. The crossover point depends on data size, operation complexity, and how many elements you ultimately consume.

Profile before optimizing. Do not assume sequences are better. Measure.

Sequences shine when the savings from laziness exceed the overhead of laziness.

Sequences can be infinite.

generateSequence 0 open brace it plus 1 close brace generates 0, 1, 2, 3, forever.

You cannot call toList on an infinite sequence. It will never finish.

Use terminal operations that limit consumption: take 10 dot toList, first with condition, any with condition.

Infinite sequences model unbounded streams. Numbers without end. Random values. Input lines from a file.

The laziness is essential. Without it, infinite is impossible.

Let us summarize.

Collections are eager. They process all elements immediately.

Sequences are lazy. They process elements on demand.

Create with asSequence, sequenceOf, generateSequence.

Intermediate operations build a pipeline. Terminal operations execute it.

Sequences process vertically, enabling early termination.

Use sequences for large data, chained operations, and partial consumption.

Avoid sequences for small data where overhead exceeds benefit.

Sequences enable efficient processing without intermediate collections.