[Android A..Z] Flow 중간연산자 정리

2024. 4. 21. 19:23개발/[Kotlin] 안드로이드 개발

반응형

flow의 중간연산자는 flow에 대한 변환을 수행하며, 그 결과로 새로운 flow를 반환한다.

이러한 중간연산자들은 종단 연산자가 호출될 때까지 실제 작업을 수행하지 않는 지연(lazy)특성을 가지고 있다.

 

지연(lazy)이란?

실제 데이터가 수집되기 시작할 때까지 주간 연산자의 작업이 실행되지 않는 것을 말한다.

 

val flow = flowOf(1, 2, 3).map {it * 2}
flow.collect { println(it) }

예를들어 1,2,3 값을 가지고 있는 스트림에 대해 map 연산자를 통해 변환한다고 가정한다.

위 코드에서 map함수는 종단 연산자인 collect()함수가 호출될 떄까지 아무런 동작도 하지 않는다.

이렇게 지연 작업을 통해 계산을 미룸으로써 리소스 사용량을 최적화한다.

FLOW API 에서 제공하는 모든 중간 연산자들은 이런 지연 방식을 따른다.

 

중간연산자

map

val numbers = flowOf(1, 2, 3)
val squares = numbers.map { it * it } // 결과: 1, 4, 9

각 아이템에 대해 함수를 적용하고, 그 결과를 포함하는 새로운 flow 생성

filter

val numbers = flowOf(1, 2, 3)
val evenNumbers = numbers.filter { it % 2 == 0 } // 결과: 2

주어진 조건에 맞는 아이템만 포함하는 새로운 flow 생성

transform

val strings = flowOf("a", "b", "c")
val transformedFlow = strings.transform { value ->
    emit(value.toUpperCase())
    emit(value.toLowerCase())
} // 결과: A, a, B, b, C, c

각 아이템에 대해 원하는 변환 작업을 수행할 수 있게 해준다.

여러 개의 값 또는 다른 타입의 값으로 변환할 때 유용하게 쓸 수 있다.

take

val numbers = flowOf(1, 2, 3)
val firstTwoNumbers = numbers.take(2) // 결과: 1, 2

처음 n개의 아이템만 포함하는 새로운 flow 생성

drop

val numbers = flowOf(1, 2, 3)
val lastNumber = numbers.drop(2) // 결과:3

처음 n개의 아이템을 제외한 나머지를 포함하는 새로운 flow 생성

distinctUntilChanged

 val repeatedNumbers=flowOf(1 ,1 ,2 ,3 ,3)
 val distinctFlow=repeatedNumbers.distinctUntilChanged()//결과 :1 ,2 ,3

이전 아이템과 같지 않은 경우에만 해당 아이템을 방출하는 새로운 flow를 생성

flatMapConcat(순차적)

val flow = flowOf(1, 2, 3)

val flatMapConcat = flow.flatMapConcat { value ->
    flow {
       emit(value)
       delay(1000)
       emit(value * value)
    }
}

1, 2, 3이라는 아이템을 가진 flow에 대해 flatMapConcat 연산자를 이용해 새로운 flow를 생성한다.

flowOf 내부에서는 현재 value와, value * value에 대한 아이템을 방출하는 flow를 생성한다.

value값에 대한 1에 대한 변환이 모두 처리된 후 2, 3이 처리되는 것을 알 수 있다.(순차적으로 실행)

flatMapMerge(병렬적)

각 원본 아이템에 대해 새로운 flow를 생성하고 이들을 병렬로 처리한 다음 순차적으로 합친다.

fun <T, R> Flow<T>.flatMapMerge(
    concurrency: Int = DEFAULT_CONCURRENCY,
    transform: suspend (value: T) -> Flow<R>
): Flow<R>

시그니처는 위와 같으며 concurrency 병렬로 처리할 동시 실행 스트림의 최대 개수를 지정한다

flatMapConcat과 다르게 병렬로 처리하기 때문에 시간상 더 효율이 좋다.

val flow = flowOf(1, 2, 3)

val flatMapMerge = flow.flatMapMerge { value ->
         flow {
           emit(value)
           delay(1000)
           emit(value * value)
        }
    }
// result
// 1 2 3 1 4 9

1,2,3에 대해 아이템이 발행을 기다리지 않는다. (아이템 2는 1에 대해 변환이 완료되는 것을 기다리지 않음)

순서가 중요하지 않을 땐 flatMapConcat보다 flatMapMerge를 사용하면 더 빠른 속도로 처리할 수 있다.

flatMapLatest(최신데이터만)

각 연산자에 대해 새로운 flow를 생성하되, 최신의 데이터만을 가지고 변환하게 끔 한다. (새로운 원본 아이템이 방출될 때 마다 이전에 생성된 flow의 실행 중단)

 val flow = flowOf(1, 2, 3)

 val flatMapLatestFlow = flow.flatMapLatest { value ->
     flow {
         emit(value)
         delay(1000)
         emit(value * value)
     }
 }
 // result
 1 2 3 9

이전 데이터가 변환이 채 끝나기 전에 새로운 데이터가 들어오면 이전 데이터에 대한 처리가 취소된다.

Process

- 아이템 1의 value가 방출된다

- 1초간 대기한다

- 새로운 아이템 2가 들어오고 기존 flow가 중단되고 새로운 데이터 2가 방출된다.

- 따라서 1*1은 실행되지 않는다

- 계속해서 반복한다

- 3의 경우 더 이상 데이터가 들어오지 않으니 3*3의 value까지 방출된다.

 

반응형