KotlinでAndroid-No.06(コレクション)

対象バージョン:Android Studio v4.1, Kotlin v1.4

コレクション

今回はKotlinのコレクション(配列やリストなど)について確認していきます。

今回も全て細かくというより、自分が利用する部分について見ていく事とします。

前置き

いつものことながら、ここで紹介している内容は、僕自身がKotlinでプログラミングする上で必要と思う事を記載しています。(ほぼ備忘録です)

間違いや、不足部分、正しくない使い方も多々あると思いますので、記載内容に疑問がある場合はKotlinの正式なサイトや、他の方の記事も参考にしてみてください。

配列(Array)

宣言

配列の宣言は下記の様に行います。(初期値なしの場合は型宣言が必須)

// 型を指定して配列を宣言
val arrInt0 : Array<Int> // 整数型配列
  
// 基本型の配列
val arrInt1 : IntArray  // Int型配列
val arrByte : ByteArray // Byte型配列
val arrShort : ShortArray // Short型配列
val arrLong  : LongArray // Long型配列
val arrFloat  : FloatArray // Float型配列
val arrDouble : DoubleArray// Double型配列
val arrChar : CharArray // Char型配列

配列は「val」で宣言しても、各配列要素の値は更新できます。
「var」で宣言する場合は、その変数名に別の配列を割り当てるようなケースになります。

また、Array<Int>とIntArrayは同じInt型の配列になりますが、異なる型になります。型変換する場合は、toIntArray()やtoTypedArray()などを用います。

初期化

配列についても初期化をしなければ利用できません。
また、初期化の方法もいくつか存在します。

Array<T>の場合

// arrayOf()による初期化
// 要素を指定して初期化(各要素の型はそろえる)
val iArr1 : Array<Int> = arrayOf(0, 1, 2, 3) // サイズ4の配列
val dArr1 = arrayOf(1.0, 1.0, 2.0)  // 型(Array<Double>)は省略可能
 
// Array()による初期化
// サイズと各要素を値や式で指定
val iArr2 : Array<Int> = Array(10, {-1}) // サイズ10、各要素を-1で初期化
val iArr3 : Array<Int> = Array(10, {i -> i}) // サイズ10、各要素をインデクスと同じ値で初期化 (0, 1, 2, 3, ...)
val iArr4 : Array<Int> = Array(10, {i -> i*i}) // サイズ10、各要素をインデクスの二乗値で初期化 (0, 1, 4, 9, ...)

XXXArrayの場合

// XXXArray()による初期化
// サイズ指定して初期化(各要素は0で初期化)
val iArr5 : Array<Int> = Array(10) // サイズ10、各要素を0で初期化
// サイズと各要素を値や式で指定
val iArr6 : Array<Int> = Array(10, {i -> i*2}) // サイズ10、各要素をインデクスの2倍の値で初期化 (0, 2, 4, 6, ...)

// arrayOf() + toXXXArray()による初期化
// 要素を指定して初期化(各要素の型はそろえる)
val iArr7 : IntArray = arrayOf(1, 2, 3).toIntArray() // サイズ3

各要素へのアクセス

他の言語と同じ様に[]を付けて、読み取り・値の設定を行います。

// 読み込み
val elem0 = iArr1[0]
// 値の設定
iArr1[0] = 999

配列のサイズを超えて要素にアクセスしようとすると、「java.lang.ArrayIndexOutOfBoundsException」例外が発生します。

配列サイズの取得

配列のサイズ(要素数)の取得は、「.size」を利用します。

forループによる要素へのアクセス

各要素を読み取るだけの場合は、「in」の後に配列名を指定します。

// 配列の各要素の出力
for(i in iArr1)
{
    // iには配列iArr1の要素が格納される
    editTextResult.append(i.toString() + System.lineSeparator())
}

上記の場合、「i」は再割り当て不可なので、配列の要素の値を変更する事は出来ません。

各要素の値を変更したい場合は、「in」の後に「配列.indices」を指定し、インデクスを取得して配列にアクセスします。

// 配列のインデックスでloop
for(i in iArr1.indices)
{
     // 配列の要素を更新
    iArr1[i] = i * 2
}

リスト(List)

Kotlinのリストは、Immutable(不変)、Mutable(可変)の区別があるので注意が必要です。

宣言

リストの宣言は下記の様に行います。

// Mutable(変更可能な)リストの宣言
val mList1 : MutableList<Int>

// Immutable(変更不可能な)リストの宣言
val iList1 : List<Int>       

Immutbleなリストとして宣言すると、要素の値の変更や、要素の追加・削除などが出来なくなります。

初期化

初期化についても、Mutable, Immutableそれぞれで記述方法が異なります。

// Mutableの場合
// mutableListOf()で列挙して初期化
val mList1 : MutableList<Int> = mutableListOf(1, 2, 3)
// MutableList<T>()でサイズと値・式を用いて初期化
val mList2 : MutableList<Int> = MutableList<Int>(10, {0}) // サイズ10, 各要素を0に初期化
val mList3 : MutableList<Int> = MutableList<Int>(10, {i -> i * 2}) // サイズ10, 各要素をインデクスx2で初期化

// Immutableの場合
// listOf()で列挙して初期化
val iList1 : List<Int> = listOf(1, 2, 3)
// List<T>()でサイズと値・式を用いて初期化
val iList2 : List<Int> = List<Int>(10, {0}) // サイズ10, 各要素を0に初期化
val iList3 : List<Int> = List<Int>(10, {i -> i * 2}) // サイズ10, 各要素をインデクスx2で初期化

各要素へのアクセス

リストの各要素へのアクセスは配列と同じです。

ただし、ImmutableなListの要素は更新が出来ません。

要素の追加・削除

MutableListへの要素の追加はadd()、削除はremoveAt()を利用する。

val mList1 : MutableList<Int> = mutableListOf(1, 2, 3)

// 追加
mList1.add(10)    // リストの最後に[10]を追加 → (1,2,3,[10])
mList1.add(3, 5)  // インデクス3の位置に[5]を追加 → (1,2,3,[5],10)

// 削除
mList1.removeAt(0) // 最初の要素を削除 → (2,3,5,10)

リストのサイズの取得

リストのサイズの取得は配列と同様に「.size」を利用します。

forループによる要素へのアクセス

forループによる要素のアクセスは、配列と同じであるため、記載は省略します。

Immutable ⇔ Mutable List

Immutable ListからMutable List、Mutable ListからImmutable Listを取得するには、下記の様にします。

// Immutable List
val iList1 : List<Int> = listOf(1, 2, 3)
// iList1からMutable Listを取得
val tmpList1 = iList1.toMutableList()

// Mutabl List
val mList1 : MutableList<Int> = mutableListOf(1, 2, 3)
// mList1からImmutable Listを取得
val tmpList2 : List<Int> = mList1
// もしくは
val tmpList3 = mList1.toList()

※当然ですが、一度宣言した型を別の型に変換は出来ませんので、別の変数を作成する事になります。

マップ(Map)

Kotlinのマップ(辞書型)は、リストと同様にImmutable(不変)、Mutable(可変)の区別があるので注意が必要です。

宣言と初期化

キーと値の組み合わせは、

<key> to <value>

で表現します。

// Immutable Mapの宣言と初期化
val imap : Map<Int, String> = mapOf(0 to "abc", 2 to "ghi")

// Mutable Mapの宣言と初期化
val mmap : MutableMap<Int, String> = mutableMapOf(0 to "abc", 2 to "ghi")

キーの追加・更新・削除

マップ内に存在しないキーを指定すればキー・値のペアを新規追加、キーが存在していればそのキーでの値の更新になります。

// Mutable Mapの宣言と初期化
val mmap : MutableMap<Int, String> = mutableMapOf(0 to "abc", 2 to "ghi")

// 新規追加(マップ内に存在しないキーを指定)
mmap[1] = "def"  // (0, "abc"), (1, "def"), (2, "gfi")

// 更新
mmap[0] = "ABC"  //  (0, "ABC"), (1, "def"), (2, "gfi")

// 削除
mmap.remove(2)  //  (0, "ABC"), (1, "def")

forループによる要素へのアクセス

.keysを用いてキー一覧を取得してアクセスします。

// 一覧表示
for(k in mmap.keys)
{
     editTextResult.append("keys = " + k + ", value = " + mmap[k] + System.lineSeparator())
}

キーはソートされている訳ではないので、もし、キーをソートした形で取得したい場合は、「mmap.keys.sorted()」の様にします。

セット(Set)

セットはリストに似ていますが下記の様な特徴があります。

  • 要素の重複が許可されない
  • 順序がない(インデクスでのアクセスが出来ない)
  • Mutable(可変), Immutable(不変)が存在する

宣言と初期化

// Mutable Setの宣言と初期化
val strMSet : MutableSet<String> = mutableSetOf("aaa", "bbb", "ccc")

// 同じ要素がある場合、1つしか登録されない
val strMSet2 : MutableSet<String> = mutableSetOf("aaa", "bbb", "bbb") // {"aaa", "bbb"}

// Immutable Setの宣言と初期化
val strISet : Set<String> = setOf("aaa", "bbb", "ccc")

要素の追加

セットに要素を追加する場合は、add()を利用します。
セットに既に登録済の要素を追加するとadd()メソッドはfalseを返します。

// Mutable Setの宣言と初期化
val strMSet : MutableSet<String> = mutableSetOf("aaa", "bbb", "ccc")

val addReslut = strMSet.add("ddd") // true,  {"aaa", "bbb", "ccc", "ddd"}
addReslut = strMSet.add("aaa") // false,  {"aaa", "bbb", "ccc", "ddd"}

便利な機能

Kotlinのコレクションの便利な機能を下記に記載します。

※頑張ってコードを書けば実現できる機能です。言語がサポートする機能を利用する事でコード量の削減やバグの混入の可能性を低減できるかも知れません。
また、他の言語に無い機能については、どうやって実現すればよいかを考えるのも勉強になるかも知れません。

in

コレクションに指定の値が含まれているかをチェックするのに利用します。

val mList1 : MutableList<Int> = mutableListOf(1, 2, 3)

val chkVal = 1
if(chkVal in mList1)
{
  	// chkValはmList1の要素の中に存在する
     editTextResult.append(chkVal.toString() + " is included in mList1" + System.lineSeparator())
}
else
{
  	// chkValはmList1の要素の中に存在しない
     editTextResult.append(chkVal.toString() + " is NOT included in mList1" + System.lineSeparator())
}

count

引数なしで利用した場合は、.sizeと同じになりますが、条件式を指定すれば、条件式を満たす要素数を取得できます。

val mList1 : MutableList<Int> = mutableListOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

// 全要素数を取得
editTextResult.append( "count() : " + mList1.count() + System.lineSeparator()) // 10

// 条件を満たす要素数を取得
editTextResult.append( "count{i>5} : " + mList1.count{it > 5} + System.lineSeparator()) // 5

※条件式を指定する場合は「()」ではなく「{}」で条件式を囲みます。

minOrNull, maxOrNull

コレクション中の最小値、最大値を求めるminOrNull(), maxOrNull()が既に用意されています。
※min(), max()はdeprecated(非推奨)になっています。

val mList1 : MutableList<Int> = mutableListOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

// 最小値の取得
editTextResult.append( "min : " + mList1.minOrNull() + System.lineSeparator()) // 1
// 最大値の取得
editTextResult.append( "max : " + mList1.maxOrNull() + System.lineSeparator()) // 2

※コレクションのサイズが0の場合、nullが変えるのではなく「IndexOutOfBoundsException」例外が発生するので注意が必要です。

filter

filterはコレクションから指定の条件を満たす新しいコレクションを作成します。

val mList1 : MutableList<Int> = mutableListOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

// Filterを使って、条件を満たす要素だけのコレクション(リスト)を作成
val mList2 = mList1.filter{x -> x > 5 }  // 各要素xに対する条件で記述 (6,7,8,9,10)
val mList3 = mList1.filter{it < 5 } // 「it」 notationの利用 (1,2,3,4)

※「it」表記を使うと少しだけ簡単に書けます。

map

mapは既存のコレクションの各要素に対して、所定の処理を行って新しいコレクションを作成します。
※前述の「Map」とは異なります。

val iList1 : List<Int> = listOf(1, 3, 3, 4, 5)

// Mapを利用し、各要素を1/2にした浮動小数点型のコレクションを取得する
val iList2 = iList1.map{x -> x.toFloat() / 2 }  // Float型 (0.5F, 1.5F, 1.5F, 2.0F, 2.5F)
// Mapを利用し、各要素が条件(3以下)を満たすかどうかのBoolean型のコレクションを取得する
val iList3 = iList1.map{it <= 3 }              // Boolean型 (true, true, true, false, false)

※型を意識した方がいいかも知れません。

find

findはコレクションの中から指定の条件を満たす最初の要素を返します。
該当の要素が存在しない場合はnullを返すため、型は「?」付き(nullable)になります。
※最後要素を取得したい場合はfindLast{}を使います。

val iList1 : List<Int> = listOf(1, 2, 3, 4, 5)

// 5未満の最初の要素
var res : Int? = iList1.find{it < 5}  // 「1」

// 5より大きい最初の要素
red = iList1.find{it > 5} // null (存在しない)

// 文字列の場合
val sList = listOf("abc", "def", "gfi")
// "abc"に一致する最初の要素を取得
val res2 : String? = sList.find{it.contentEquals("abc")}

文字列の検索の場合、下記の様な条件が利用出来ます。
※if文などの条件判定でも利用は可能です。

記述意味
it.contentEquals(“abc”)“abc”に一致するもの
it.contains(“ab”)“ab”を含むもの
it.startWith(“a”)“a”で始まるもの
it.endWith(“c”)“c”で終わるもの
it.match(“*”)正規表現(“*”)にマッチするもの

sorted

sortedを利用する事で、昇順にソートしたコレクションを取得できます。
並び替えを指定したい場合は、sortedBy{}を利用します。

val iList1 : List<Int> = listOf(9, 5, 1, 7, 3)

val iList2 = iList1.sorted() // 昇順にソート (1, 3, 5, 7, 9)
val iList3 = iList1.sortedBy {-it} // 降順にソート (9, 7, 5, 3, 1)

any / all / none

any(一つでも条件に一致)、all(全てが条件に一致)、none(一つも条件に一致しない)を判定するために利用します。

val iList1 : List<Int> = listOf(9, 5, 1, 7, 3)

// any{} : 少なくとも1つの要素が条件を満たすか
val result1 : Boolean = iList1.any{it % 2 == 0} // false (偶数の要素がある)
// all{} : すべての要素が条件を満たすか
val result2 : Boolean = iList1.all{it % 2 == 1} // true (全て奇数)
// none{} : 条件を満たす要素が一つもない
val result3 : Boolean = iList1.none{it % 2 == 0} // true (まったく偶数の要素がない)

補足

コレクションの初期化や条件式の記述について、it表記を使う方法と使わない方法があります。下記は同じ内容となります。

※it表記を使わない場合、利用する文字(列)は任意です(予約語以外)

初期化方法

// {0, 2, 4, 6, 8, 10, 12, 14, 16, 18}
val iList1 : List<Int> = List(10, {x -> x * 2})
val iList2 : List<Int> = List(10, {it * 2})

条件設定

// Filterでの条件設定
val mList2 = mList1.filter{x -> x > 5 }  // 各要素xに対する条件で記述 (6,7,8,9,10)
val mList3 = mList1.filter{it < 5 } // 「it」 notationの利用 (1,2,3,4)

条件を記載する場合は{}

関数・メソッドの呼び出しは「count()」の様になりますが、条件式を記載する場合は、「count{ it > x }


さて、今回はKotlinのコレクションについて見てきました。

Kotlinのコレクションについては、Mutable / Immutableの区別を注意した方がよさそうですね^^;

次回予告

次回は関数・メソッドについて見て行きたいと思います。


前の記事次の記事
No.05(フロー制御)No.07(関数・メソッド)