skip to content

What does the conflate() operator do to a Kotlin Flow, and when would you reach for it?

level: juniorimportance: must knowfreq 55%

answer

  1. Keep latest, drop the middle
  2. = buffer(1, DROP_OLDEST)
  3. Producer never waits for collector
  4. Latest-state rendering (progress, prices)
  5. Lossy by design — not for must-process flows

basics

~20 s

conflate() lets a fast producer skip ahead: if the collector is busy, in-between values are dropped and it only gets the newest one. Good when you only care about the latest value, like a live counter.

solid answer

~40 s

conflate() decouples the producer from a slow collector and keeps only the most recent emission. While the collector is processing a value, the upstream keeps emitting; intermediate values are dropped and when the collector is ready it receives the latest one. It never suspends the producer waiting for the collector. It is shorthand for buffer(capacity = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST) (conceptually buffer(Channel.CONFLATED)). Typical use: latest-state rendering — UI progress bars, live prices, sensor readings — where stale intermediate values are useless. You lose values by design, so it is wrong for flows where every emission must be processed (e.g. payments, audit logs). Like buffer(), it introduces a separate coroutine so producer and collector run concurrently.

code

kotlin · 8 lines
kotlin
flow {
    repeat(5) { i -> delay(100); emit(i) }
}
    .conflate()
    .collect { i ->
        delay(300)            // slow collector
        println("got $i")     // got 0, got 2, got 4 ...
    }

go deeper

for a junior

Can state that conflate() keeps the latest value and drops the in-between ones, and names a UI/progress use case.

for a middle

Explains the producer/collector concurrency and that it is buffer(1, DROP_OLDEST); knows it is lossy.

for a senior

Frames it as a backpressure strategy, contrasts with non-dropping buffering, and reasons about when data loss is acceptable.

for a principal

Discusses it as a system-level latency/throughput trade-off and where conflation belongs in a UI-state vs event-stream architecture.

## What conflate() is `conflate()` is a Flow operator (from `kotlinx.coroutines.flow`) that addresses **backpressure** — the situation where a producer emits values faster than the collector can process them. Normally a `Flow` is *sequential and synchronous*: the producer **suspends** until `collect` finishes handling the current value, so a slow collector slows the whole pipeline. `conflate()` breaks that coupling. With `conflate()`, the producer and collector run **concurrently** (a buffer/channel sits between them). While the collector is busy with one value, the producer keeps running; any values it emits in the meantime overwrite each other so only the **most recent** survives. When the collector becomes free, it receives that latest value and everything emitted in between is **silently dropped**. ## The key behaviour - It **never blocks/suspends the producer** to wait for the collector. - It **drops intermediate values** — this is intentional data loss. - The collector always eventually sees the **latest** emission, never a stale one. ## Equivalence to buffer `conflate()` is exactly: ```kotlin buffer(capacity = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST) ``` (conceptually `buffer(Channel.CONFLATED)`). When a new value arrives and the 1-slot buffer is full, the *oldest* buffered value is dropped in favour of the newcomer — hence "keep the latest". ## When to use it Use it for **latest-state rendering**, where intermediate values are worthless once superseded: - progress bars / download percentage - live stock prices, exchange rates - sensor or GPS readings - a UI that just needs to paint the current state ## When NOT to use it If **every** emission matters (payment events, log lines, analytics), conflation silently loses data and is a bug. Use plain collection or `buffer()` with a non-dropping strategy instead. ## Tiny example ```kotlin flow { repeat(5) { i -> delay(100) // fast producer emit(i) } } .conflate() .collect { i -> delay(300) // slow collector println("got $i") } // Prints something like: got 0, got 2, got 4 — middle values dropped. ``` Here the collector is ~3x slower, so by the time it finishes one value, newer ones have replaced the queued one.

  • Does conflate() drop the FIRST or the LAST value when it overflows?
    It drops the older (already-buffered) value and keeps the newer one — DROP_OLDEST — so the collector always ends up with the most recent emission.
  • Will the collector always receive the very last value the producer emits?
    Yes — even if intermediate values are dropped, the final emission is delivered (assuming the flow completes and the collector is given a chance to process it).

Like a glance at a speedometer: you only read the current speed, you don't care about every value the needle passed while you looked away.

saying these in an interview costs you the question

  • Says conflate() buffers all values (it keeps only one)
  • Thinks it blocks the producer until the collector catches up
  • Recommends it for flows where every event must be processed
  • Confuses it with distinctUntilChanged (which dedupes, not drops-for-speed)

context