Arrays
Arrayは『配列』を意味する英単語であり、KotlinにもArray(配列)を扱うためのArrayというClassが用意されています。
ですが、他のプログラミング言語における配列と同じような感じで扱える『一般的な配列』は、ArrayではなくCollection(のMutableList)なので、Arrayの存在感は薄いです。
それもあってか、Kotlinの公式ページ(https://kotlinlang.org/)のチュートリアルにも、Arrayは出てきません。(Collectionは出てきます)
なので、どうしても『KotlinにはCollectionしか存在しない(Arrayという言葉や概念はない)』という誤解をしてしまいがちです。
KotlinにおいてArrayの重要度はそこまで高くないのですが、間違った理解をしてしまわないためにも、まずArrayから簡単に紹介していきます。
KotlinでArrayを宣言するには、arrayOf()を使います。(他にも色々方法はありますが、この記事では一番オーソドックスなものを紹介していきます)
下は、KotlinでArray(配列)を作成する記述例です。
val myArray = arrayOf(1,2,3,4)
myArray.forEach {
println(it)
}
// 1
// 2
// 3
// 4
特定の要素を参照したい場合は、他のプログラミング言語と同様、要素番号(インデックス)を[ ](角括弧)で指定すればOKです。
val myArray = arrayOf(1,2,3,4)
println(myArray[2]) // 3
さらに、一般的な配列と同様に、値を更新することもできます。
val myArray = arrayOf(1,2,3,4)
myArray[2] = 8
myArray.forEach {
println(it)
}
// 1
// 2
// 8
// 4
ここまでであれば、arrayOf()という関数を使って配列を宣言するということ以外、他のプログラミングの配列とほぼ同じかのように思えます。
ただし、実は宣言方法以外にも、一般的な配列とは決定的に違う点があります。
その違いとは、サイズ(配列の要素数)を後から変更できないというものです。
配列の要素数を、Kotlinではsizeと表現します。
普通、配列といえば配列を宣言した後からでも要素を追加したり、削除したり…といった操作を行えるものだと思いますよね。
実際、多くのプログラミング言語では配列に対してそういった操作が可能です。
Kotlinでも後述するMutableListなどであればそういった操作が可能なのですが、Arrayに関してはsizeが変更できないようになっています。
なのでArrayは、値を変更する必要はあるが、サイズを変更する必要がない(変更できない方が良い)配列を宣言したい場合に便利です。
例えば、ゲームにおいて『装備は最大で5個まで身につけられる』というシステムにする場合、装備品(要素の値)は変更可能である必要がありますが、装備の最大数(サイズ)は5で固定された方が都合が良いですよね。
このような場合、サイズが変更可能な配列で管理するより、サイズが固定である配列(KotlinではArray)で管理した方が、バグや設定ミスを防止しやすくなります。
とは言え、一般的には配列のサイズを柔軟に変更できるようにしたい場合の方が多いかと思います。
例えば、ユーザーIDや商品コードを管理するための配列は、ユーザー数や商品数が増減するたびに配列のサイズが更新される必要があります。
では、Kotlinでサイズを変更可能な配列を作成したい場合はどうすれば良いのでしょうか?
次のステップでは、要素の追加や削除が可能な、いわゆる『一般的な配列』をKotlinで定義する方法をご紹介します。
POINT!
- ・Kotlinにおける配列はCollectionが有名だが、Arrayが存在しないわけではない!
- ・arrayOf()で、配列を定義することができる!
- ・Arrayは、要素の値を変更することは可能だが、要素の数(サイズ)は変更不可!
MutableList
KotlinにはArraysの他にCollectionsと呼ばれるグループが存在します。
Arrayはその実態がClassであるのに対し、Collectionsの実態はInterfaceです。(基本的には)
細かいところまで気にしだすと混乱してしまうので(筆者が)、ここではとりあえず、『どちらもよく似ているけど異なるもの』…という程度のイメージを持って頂ければ十分です。
そして、Collectionsに含まれるものの中に、MutableListというものがあります。
これこそが、他のプログラミング言語における一般的な配列の位置付けになります。
Arrayは配列のサイズを変更することができませんでしたが、MutableListであれば、サイズを変更することが可能です。
MutableListでは、他のプログラミング言語でもよく見かける、add()とかremove()といった、要素の追加・削除を行う関数もちゃんと用意されています。
KotlinでMutableList形式の配列を宣言するには、mutableListOf()を使います。
下はmutableListOf()でMutableListを作成し、さらにadd()で配列の末尾に要素を追加する例です。
var myList = mutableListOf(1,2,3,4)
myList.add(5)
println(myList) // [1,2,3,4,5]
そしてこちら(下)は、removeAt()で配列の要素番号(インデックス)が3の要素を削除する例です。
var myList = mutableListOf(1,2,3,4)
myList.removeAt(3)
println(myList) // [1,2,3]
もちろん、Arrayと同様に[ ](角かっこ)を使って配列のインデックスを指定し、要素を参照したり、値を書き換えたり…といったことも可能です。
ということで、これまでにも既に述べているように、MutableList = 他のプログラミング言語における一般的な配列…と考えて差し支えありません。
ちなみにですが、Mutable(可変)でないListも存在します。
MutableがつかないListは読み取り専用で、値を書き換えたりといった操作はできません。
こちらは、データの数も値も決まっていて、いずれも外部から変更されたくないリストを作成したい場合に使われます。
このように、Kotlinでは目的や状況に合った形式の配列(Collection)がプログラミング言語側でしっかり用意されているのが大きな特徴です。
(Mutable)Listの他にも、並び順が関係なく、値が一意であることが求められる(Mutable)Set、一意のkeyとvalueという関係を持つ(Mutable)Mapなどもあります。
お手軽かつ自由に配列を作成したり、操作したり…といったことには制約がある反面、構造的に事故や不具合が生じにくいと言えます。
Kotlinでは、データの集まりを全て『配列』としてひっくるめて捉えるのではなく、データの構造や使用状況に着目して、それに適した『Collection』という形で捉え、理解することが重要です。
KotlinのCollectionについてもっと理解を深めるために、次のステップでは、(Mutable)Setの基本を確認していきましょう!
POINT!
- ・MutableListは、配列のサイズが変更可能な、いわゆる一般的な配列として利用できる!
- ・MutableListは、mutableListOf()で宣言(定義)できる!
- ・MutableでないListは読み取り専用であり、データの変更や操作はできない!
MutableSet
MutableSetはMutableListと同様に可変であり、後からデータを追加したり、削除したりすることができますが、配列のように『順番』という概念がありません。
また、値は一意であることが求められ、重複は許可されません。
なので、『順番を必要としない、一意のデータ群を管理したい場合』に便利です。
たとえば、『これまでに訪れたことがある国リスト』を作る場合で考えてみましょう。
初めて訪れた国、〇番目に訪れた国…というふうに、訪れた順番で管理したい場合は配列(MutableList)で管理した方が良いですが、単に『訪れたことがある国々』として管理したいのであれば、順番は必要ありません。
こんな時こそ、MutableSetの出番です。
MutableSetはデータをインデックス(順番)で管理するわけではなく、データは一意であることが求められるので、『そういえば、あの国も訪れたことがあるんだった』と、後から思い出して追加しても問題ありませんし、間違えて同じ国がリスト内で重複してしまうこともありません。
MutableSetを作成したい場合は、mutableSetOf()を使って次のように宣言します。
var mySet = mutableSetOf("USA","Japan","Australia","Spain")
println(mySet) // [USA, Japan, Australia, Spain]
配列のように順番で管理しないため、下のようにインデックス番号で指定してデータを参照することはできません。
var mySet = mutableSetOf("USA","Japan","Australia","Spain")
println(mySet[1]) // Error
Mutable(可変)であるため、add()やremove()でデータに変更を加えることは可能です。
var mySet = mutableSetOf("USA","Japan","Australia","Spain")
mySet.add("Canada")
mySet.remove("Spain")
println(mySet) // [USA, Japan, Australia, Canada]
重複したデータをadd()で加えようとしてもエラーにはなりませんが、Setのデータは重複しないようになっています。
var mySet = mutableSetOf("USA","Japan","Australia","Spain")
mySet.add("Canada")
mySet.remove("Spain")
mySet.add("Canada")
println(mySet) // [USA, Japan, Australia, Canada]
なので、ヒューマンエラーにより重複したデータが追加されるような操作が起こったとしても、安心して重複していないデータ構造として扱うことが可能です。
ちなみにですが、MutableでないSetも用意されており、こちらはMutableでないListと同様に読み取り専用です。(データの追加や削除はできません)
POINT!
- ・(Mutable)Setには、配列のような順番という概念がない!
- ・(Mutable)Setの値は重複が許可されていない!
- ・MutableSetに重複した値を格納しようとしても、データが重複されることはない!
MutableMap
少し複雑な構造のデータを管理したい場合、いわゆる一般的な形式の配列(MutableList)では難しいことがあります。
そのような時は、key-valueの関係をもったリストを作成できるMutableMapが便利です。
JavaScriptの経験がある方は、MutableMap ≒ JavaScriptオブジェクトのような構造…というイメージを持つと分かりやすいかもしれません。
keyの値は重複が許されませんが、valueにはそのような制約はありません。
MutableMapを作成するには、mutableMapOf()を使って、下のようにtoでkeyとvalueを紐づけます。
var myMap = mutableMapOf("apple" to 100, "orange" to 120, "banana" to 150)
println(myMap) // {apple=100, orange=120, banana=150}
なお、MapもSetと同様に、順番のある配列構造になっているわけではありません。
なので、下のようにインデックス番号でデータを参照することはできません。
var myMap = mutableMapOf("apple" to 100, "orange" to 120, "banana" to 150)
println(myMap[1]) // Error: Type inference failed.
データの参照は、下のようにkeyで行うのが基本です。
var myMap = mutableMapOf("apple" to 100, "orange" to 120, "banana" to 150)
println(myMap["apple"]) // 100
MutableMapへのデータの追加は、set(key,value)で行うことができ、削除はremove(key)で行えます。
var myMap = mutableMapOf("apple" to 100, "orange" to 120, "banana" to 150)
myMap.set("lemon", 200)
myMap.remove("apple")
println(myMap) // {orange=120, banana=150, lemon=200}
keysとvaluesプロパティで、keyのみ、またはvalueのみにアクセスすることも可能です。
var myMap = mutableMapOf("apple" to 100, "orange" to 120, "banana" to 150)
println(myMap.keys) // [apple, orange, banana]
println(myMap.values) // [100, 120, 150]
二つ以上のMapをListにしてまとめれば、順番のある配列としてMap構造のデータを管理することもできます。
var myFruits = mutableMapOf("apple" to 100, "orange" to 120, "banana" to 150)
var myVegetables = mutableMapOf("tomato" to 110, "carrot" to 130, "onion" to 140)
var myFoods = listOf(myFruits,myVegetables)
println(myFoods[0].keys) // [apple, orange, banana]
println(myFoods[0].values) // [100, 120, 150]
println(myFoods[1].keys) // [tomato, carrot, onion]
println(myFoods[1].values) // [110, 130, 140]
JavaScriptで言えば、オブジェクト構造のデータを[ ]内に並べて配列にした感じですね。
以上(List,Set,Map)が、Kotlinにおける配列やそれに似た構造のデータ群(Collection)の基本です。
まずは(Mutable)Listから基本的な使い方を知ることから始め、Kotlinに慣れてきたら他のCollectionも詳しく学ぶようにすると、理解しやすいのではないかと思います。
個人的に、Kotlinでは『こんな時はListじゃなくてSetの方が良いかな?』というふうに、データ構造によって使い分けを考えられるのが楽しいな〜と思っています。笑
POINT!
- ・(Mutable)Mapは、keyとvalueの関係を持つデータ!
- ・keyは一意である必要があり、valueは重複OK!
- ・2つ以上のMapをList化し、配列と組み合わせた使い方もできる!