1. 概要

このチュートリアルでは、Kotlinの map() flatMap()の微妙な違いについて説明します。

2. map()

map()は、Kotlinの拡張関数であり、次のように定義されています。

fun <T, R> Iterable<T>.map(transform: (T) -> R): List<R> 

上に示したように、この関数は、 反復可能一つずつ。 この反復中に、タイプTのすべての要素をタイプ Rの別の要素に変換します。 最後に、受信コレクションのすべての要素を変換し、最終的にはリスト

この関数は通常、1対1のマッピング状況で役立ちます。 たとえば、各注文が詳細な購入アイテムとして多くの注文ラインで構成されているとします。

class Order(val lines: List<OrderLine>)
class OrderLine(val name: String, val price: Int)

これで、 Order がある場合、 map()を使用して各アイテムの名前を見つけることができます

val order = Order(
  listOf(OrderLine("Tomato", 2), OrderLine("Garlic", 3), OrderLine("Chives", 2))
)
val names = order.lines.map { it.name }

assertThat(names).containsExactly("Tomato", "Garlic", "Chives")

ここでは、変換していますリストリスト単純な変換関数を渡すことによって。 この関数は、各 OrderLine を入力( it 変数)として受け入れ、Stringに変換します。 別の例として、注文の合計価格を次のように計算できます。

val totalPrice = order.lines.map { it.price }.sum()
assertEquals(7, totalPrice)

基本的に、 map()は、次の命令型コーディングスタイルと同等です。

val result = mutableListOf<R>()
for (each in this) {
    result += transform(each)
}

map()を使用している場合は、変換部分を作成するだけです。 新しいコレクションの定義、反復、およびそのコレクションへの各変換された要素の追加は、単なる定型コードであり、実装の詳細の一部です。

3. flatMap()

map()とは対照的に、 flatMap() は通常、1対多の関係を平坦化するのに役立ちます。 そのため、その署名は次のようになります。

fun <T, R> Iterable<T>.flatMap(transform: (T) -> Iterable<R>): List<R>

上に示したように、タイプTの各要素をタイプRのコレクションに変換します。 それにもかかわらず、 リスト >> flatMap() 各Iterableを平坦化しますその個々の要素に 。 したがって、 リスト結果として。

例として、注文のコレクションがあり、すべての個別のアイテム名を検索するとします。

val orders = listOf(
  Order(listOf(OrderLine("Garlic", 1), OrderLine("Chives", 2))),
  Order(listOf(OrderLine("Tomato", 1), OrderLine("Garlic", 2))),
  Order(listOf(OrderLine("Potato", 1), OrderLine("Chives", 2))),
)

最初は、どういうわけか変換する必要がありますリストリスト 。 使用する場合地図() ここでは、 リスト >> 、これは望ましくありません:

orders.map { it.lines } // List<List<OrderLine>>

平らにする必要があるのでリスト個人に OrderLine ここでは、 flatMap() 関数:

val lines: List<OrderLine> = orders.flatMap { it.lines }
val names = lines.map { it.name }.distinct()
assertThat(names).containsExactlyInAnyOrder("Garlic", "Chives", "Tomato", "Potato")

上に示したように、 flatMap()関数は、各OrderとそのOrderLinesの間の1対多の関係を平坦化します。

flatMap()に相当する命令型スタイルは次のようなものです。

val result = mutableListOf<OrderLine>()
for (order in orders) {
    val transformedList = order.lines
    for (individual in transformedList) {
        result += individual
    }
}

繰り返しになりますが、コレクションの初期化、反復、および平坦化は、 flatMap()の非表示の実装詳細の一部です。 私たちがしなければならないのは、変換関数を提供することだけです。

4. 結論

この記事では、Kotlinの map() flatMap()の違いを学びました。 要約すると、 map()は通常1対1のマッピングに役立ちますが、 flatMap()は1対多のマッピングを平坦化するのに役立ちます。

いつものように、すべての例はGitHubから入手できます。