KotlinでImmutableな2次元List作成

投稿者: | 2019年2月11日

Korlinでのimmutableについて

多次元配列をImmutableにしようとしてできていないのが現状である。

そこでまずKotlinにおけるImmutableとは何ぞや、と言うことについて検討してみた。

immutabilityを要求するのはFunctionalプログラミングにおいて顕著なのでその系統の書籍、例えばFunctional Kotlin1を中心に当たってみた。

Immutableとread onlyとは異なる

始めにこの事実を知っておこう。Kotlinにおいてvar宣言したものは変更でき、val宣言したものは変更できないとよく言われるが、実際の所val宣言したものがPrimitiveデータ型でなければ中身を変更することは可能である。

つまりval宣言してImmutableになるのはPrimitiveデータ型だけである。val宣言とは「read onlyとして宣言した」と言うのである。

val宣言では、どこがImmutableなのかというとそのオブジェクトへの「ReferenceがImmutableだ」と言う立場なのである。そしてread onlyではあっても様々なメソッドで中身を更新することが可能である。あとで実例を挙げる。

const valというもの

これはPrimitive型かStringに対してのみ使えるもので中身も定数である。オブジェクトのトップの属性としてしか使えず、クラスや関数の内部で宣言して使うことは出来ない。

C言語の記号定数のような使い道を想定しているのだろうか?自由度が低すぎてちょっと使えそうにない。

Immutable/Mutableなコレクション

KotlinにはArray以外にList, Set, Mapのコレクションがあり、基本はImmutableである。それぞれにMutableを冠したコレクションがあり、こちらはMutableである。

興味があるのは2次元配列をImmutableなListで実装すると言うことなので、それに沿って様々なパターンを実験してみた。

ここからはソースコードで見ていこう。

    //Immutable 1D lists
    val myIntList1 = listOf<Int>(1,2,3,4,5,6)
    val myIntList2 = listOf<Int>(7,8,9,10,11,12)

    //(Mutable) 1D arrays
    val myIntArray1 = arrayOf(1,2,3,4,5,6)
    val myIntArray2 = arrayOf(7,8,9,10,11,12)

    //Mutable 1D lists
    val myMutableIntList1 = mutableListOf<Int>(1,2,3,4,5,6)
    val myMutableIntList2 = mutableListOf<Int>(7,8,9,10,11,12)

    //Immutable 2D list
    val my2DIntList : List<List<Int>> = listOf(myIntList1,myIntList2)

    //(Mutable) 2D array
    val my2dIntArray : Array<Array<Int>> = arrayOf(myIntArray1,myIntArray2)

    //Mutable 2D list whose members are immutable
    val my2DMutableList : MutableList<List<Int>> = mutableListOf(myIntList1,myIntList2)

    //Mutable list whose members are also mutable
    val my2DMutableMutableList : MutableList<MutableList<Int>> = mutableListOf(myMutableIntList1,myMutableIntList2)

    //Immutable 2D list whose members are mutable
    val my2DImmutableMutaableList : List<MutableList<Int>> = listOf(myMutableIntList1,myMutableIntList2)

二次元Listを作成する際にImmutableかMutableかを

  1. 全体に一律
  2. 内側の一次元Listと外側とで変える

パターンがあり、都合4通りできるのである。それらは結局

  1. Immutable
    行列共にImmutableなList
  2. Mutable
    行列共にMutableなList
  3. Outside immutable, inside mutable
    新たな行を追加、存在する行を削除などは出来ないが、各一次元Listの中身は変更できる。
  4. Outside mutable, inside immutable
    新たな行を追加、存在する行を削除などはできるが、一次元Listの中身は変更できない。
    ここで注意すべきなのは、List宣言した所にMutableListを代入しても問題なくビルドされ、Mutableな振る舞いをすることである。逆にMutableList宣言した所にListを代入しようとするとエラーでビルドできない。

というものに帰着できる。FunctionalプログラミングのためにはImmutableなことが大事なので最初のものを使うことになりそう。

反転操作などの具体例

MutableなList, Arrayに行追加、反転操作を加えているサンプルプログラム。

fun main(args: Array<String>){

    //Immutable 1D lists
    val myIntList1 = listOf<Int>(1,2,3,4,5,6)
    val myIntList2 = listOf<Int>(7,8,9,10,11,12)

    //(Mutable) 1D arrays
    val myIntArray1 = arrayOf(1,2,3,4,5,6)
    val myIntArray2 = arrayOf(7,8,9,10,11,12)

    //Mutable 1D lists
    val myMutableIntList1 = mutableListOf<Int>(1,2,3,4,5,6)
    val myMutableIntList2 = mutableListOf<Int>(7,8,9,10,11,12)

    //Immutable 2D list
    val my2DIntList : List<List<Int>> = listOf(myIntList1,myIntList2)

    //(Mutable) 2D array
    val my2dIntArray : Array<Array<Int>> = arrayOf(myIntArray1,myIntArray2)

    //Mutable 2D list whose members are immutable
    val my2DMutableList : MutableList<List<Int>> = mutableListOf(myIntList1,myIntList2)

    //Mutable list whose members are also mutable
    val my2DMutableMutableList : MutableList<MutableList<Int>> = mutableListOf(myMutableIntList1,myMutableIntList2)

    //Immutable 2D list whose members are mutable
    val my2DImmutableMutaableList : List<MutableList<Int>> = listOf(myMutableIntList1,myMutableIntList2)

    println(my2DIntList)
    println(my2dIntArray[0][1])

    println(my2DMutableList)
    println(my2DMutableMutableList)

    //Reverse 1D array/list
    myIntArray1.reverse()
    myMutableIntList1.reverse()
    println(my2DIntList)
    println(my2dIntArray[0][1])
    println(my2DMutableList)

    //Put them back
    myIntArray1.reverse()
    myMutableIntList1.reverse()

    //Add a row for the 2d list
    my2DMutableList.add(listOf(13,14,15,16,17,18))
    println(my2DMutableList)
    //Reverse rows of 2d list
    my2DMutableList.reverse()
    println(my2DMutableList)
    //Put them back
    my2DMutableList.reverse()

    //Reverse rows of 2d array
    my2dIntArray.reverse()
    println(my2dIntArray[0][1])
    //Put them back
    my2dIntArray.reverse()

    //Reverse all elements
    println(my2DMutableMutableList)
    myMutableIntList1.reverse()
    myMutableIntList2.reverse()
    my2DMutableMutableList.reverse()
    println(my2DMutableMutableList)

    //Put them back
    myMutableIntList1.reverse()
    myMutableIntList2.reverse()
    my2DMutableMutableList.reverse()
    println(my2DMutableMutableList)
}

  1. Functional Kotlin, Mario Arias, Rivu Chakraborty, Packt Publishing 2018. ↩︎

コメントを残す