[Kotlin] Set型の特徴とList型との相互変換

今回はコレクション第2回、Set型についてのお話です。

SetはListと似ていて影が薄い、どこか脇役的な感じですが、Listとの違いをハッキリさせておくことで、適切な場面でSetを選択することができるはずです。

Setの特徴

SetはListと似た存在であるということは間違いありません。

どちらも複数の値を格納できるコンテナタイプの型であり、読み取り専用とMutableの2種類のバージョンを持ち、forなどの繰り返し処理に対応しています。

しかしSetにはListには無い大きな特徴が2つあります。その2つとは、

  1. 値が重複しない
  2. インデックス番号で管理されない

まずはこの2つの特徴について、実際にSetを使用して見ていくことにしましょう。

値が重複しないSet

Setの作成はsetOf、mutableSetOf関数で行います。比較のためにListも作成しておきましょう。

fun main() {
    val set = setOf(1,2,3,1,2,3)
    val mSet = mutableSetOf("サーモン","いくら","ハマチ","ハマチ")
    val list = listOf("サーモン","ねぎとろ","サーモン")
}

この3つのコレクションを標準出力すると、ListとSetとの違いが見えてきます。

//[1, 2, 3]
//[サーモン, いくら, ハマチ]
//[サーモン, ねぎとろ, サーモン]

Listの場合は2回代入したサーモンがそのまま出力されていますが、Setの場合はダブった値は登録されていないことが分かると思います。

エラーにはなりませんが、重複した値の代入は無視されるのがSetのルールです。

インデックス番号でアクセスできないSet

Setは順序を保証しないので、インデックス番号で値にアクセスすることは基本的にできません。

println(set[1])
//エラー:(7, 13) Kotlin: Unresolved reference. None of the following candidates is applicable because of receiver type mismatch: ...

未解決の参照。次の候補のどれも該当しない。なぜならレシーバの型が違うからである …(以下続く)

このUnresolved〇〇が出てくるということは、文法エラーが起きていることがほとんどです。少なくともKotlinではSetに対して、インデックス番号でアクセスする文法は用意されていません。

また値を追加するaddメソッドはmutableSetの場合使用できますが、Listのようにadd(2,"穴子")のようにインデックス番号を使った追加は許されません。

elementAtで値を参照する

基本的にインデックス番号を使用できないSetですが、値の参照であれば一応の解決策はあります。elementAtメソッドです。

println(set.elementAt(1))     //2
println(mSet.elementAt(2))     //ハマチ

elementAtメソッドは引数にインデックス番号を取り、そのインデックス番号に位置する値を出力します。

Listでも使えるメソッドですが、Setに対してelementAtを使用するのはあまりオススメではありません。

elementAtは内部的にはループ処理を行っているため、速度の面で圧倒的に遅くなります。Setが大きくなった場合、頻繁にこのメソッドを呼び出すことは避けるべきでしょう。

ListとSetの相互変換

値へのアクセスが不便なSetですが、重複した値を何も考えなくても排除してくれるメリットもあります。そこで、ListとSetとの相互変換を行うことでこの2種類のいいとこ取りに挑戦してみます。

fun main() {
    val list = listOf("いくら","サーモン","中トロ","たまご","えび","鯛")
    val mList = mutableListOf<String>()
    for(i in 1..10){
        mList.add(list.shuffled().first())
    }
    println(mList)
    val set = mList.toSet()
    println(set)
}
//[鯛, えび, 鯛, 中トロ, たまご, たまご, 鯛, サーモン, いくら, サーモン]
//[鯛, えび, 中トロ, たまご, サーモン, いくら]

まずはListからSetへの変換です。

このコードではあらかじめ寿司ネタをリストアップしておき、それをfor文で10回繰り返し、新たなListに書き加えています。

addメソッドの中で行っているのは、リストの中からランダムに値を取り出す処理です。リストをシャッフルした後で、最初の値を空のリストに書き加えています。

お客さんからの注文は当然、mListそのままのように重複した値が入ります。

しかし次の日、店の大将が市場へ仕入れに行くときには、このような重複したリストでは何を仕入れていいのか、分かりにくくてしょうがありません。

ということで、toSetメソッドを使って重複した値を削除しています。

さらに「このリストの3番目は何だったかな」となった場合は、

println(set.toList()[2])     //中トロ

toListメソッドでListに戻すと、個々の値にインデックス番号でアクセスすることができます。

おさらい

今回はSetにスポットを当て、

  • Setの特徴と作成
  • Setの要素にインデックス番号でアクセスする
  • SetとListの相互変換

についてお話しました。

次回はコレクション3種類の最後、Mapについて。

タイトルとURLをコピーしました