00. 目次
-
何らかの言語修得者用 差分で覚えるKotlin その一
変数, 分岐, ループ, 関数等のまとめ。オブジェクト指向の知識不要 -
何らかの言語修得者用 差分で覚えるKotlin その二
クラスとインターフェースのまとめ。オブジェクト指向の知識が必要 -
何らかの言語修得者用 差分で覚えるKotlin その三
現在のページ。ジェネリクスと、配列やコレクションのまとめ。オブジェクト指向の知識が必要
初見の方は"差分で覚えるKotlin その一"の「はじめに」に目を通して頂けると、何故にこんなざっくりした説明なのかお分かり頂けるかと思います。
# 12. ジェネリクス(総称型) ## ジェネリクスとは
- Wikipedia様の見解
- (自分なりにかみ砕くと) 汎用的に使えるクラスやメソッドを定義でき、それらの"型"は後から決められる方法
上記説明ではさっぱり伝わらないと思われるので、クラスの継承と比較した例を挙げる。
数学のノート,住所録,帳簿をクラスとして定義したい時
- 継承を使う場合
- 1. ノートクラス(親)を定義
- 2. 親の派生として、数学ノート,住所録,帳簿クラスを定義する
/* ノートクラス(親) */
open class Notebook(){
// プロパティ変数やメソッドをさらに記述
}
/* 数学のノートクラス(子) */
class MathNotebook() : Notebook(){
// プロパティ変数やメソッドをさらに記述
}
/* 住所録クラス(子) */
class AddressBook() : Notebook(){
// プロパティ変数やメソッドをさらに記述
}
/* 帳簿クラス(子) */
class AccountBook() : Notebook(){
// プロパティ変数やメソッドをさらに記述
}
// ノートの種類分定義を繰り返さなければならず、コード量が増える
- ジェネリクスを使う場合
- 1. 汎用ノートクラスを定義
- 2. コード内で用途に応じて汎用ノートクラスの型(=住所録や帳簿)を決める
/* 汎用ノートクラス */
// 'T'が型を指定する部分. StringやInt等既存クラスも指定できるし、自作クラスもOK
// コンストラクタの引数やプロパティ変数にも汎用型を指定できる
class Notebook<T>(){
// プロパティ変数やメソッドを記述
}// 定義はこれだけ
前者は用途毎に専用のノート(ジャ○ニカ学習帳や住所録)を用意するイメージ。
後者は大学ノートを準備して、使う際に表紙に用途(=住所録や帳簿)を書いたラベルを貼って使うイメージ。
……で伝わるでしょうか??
## ジェネリクスの構文
- ジェネリクスクラスの定義は、クラス名の後ろに
<T>
をつける
クラス名<T>(コンストラクタの引数){ ~プロパティ変数やメソッド~ }
- コンストラクタの引数やプロパティ変数にも'T'を指定できる
- 'T'は慣例で、実際は好きな文字列をつけることが出来る。下記表内のアルファベットが使われることが多い
アルファベット | 意味 |
---|---|
T | Type(型) |
E | Element(要素) |
K | Key(キー) |
V | Value(値) |
N | Number(数値) |
- 関数の引数や戻り値にも指定できる
fun <T> 関数名(引数名: T): T
- ジェネリクスクラスをインスタンス化する際は、クラス名の後ろに
<型>
をつける
/*
* 汎用ノートクラスNotebookを定義し、それを利用して住所録クラスを作成するよ
*/
/* 汎用ノートクラス */
class Notebook<T>(val title: String){
// MutableListは変更可能なリスト型コレクション
// MutableListのせいでコードがよく分からないという方は、次の章を先にお読みください
var pages: MutableList<T> = mutableListOf<T>() // ノートのページ
}
/* 住所録のデータクラス 1件=1ページという雑な設定 */
data class Address(var name: String, var address: String, var telephonenumber: String)
/* 汎用ノートクラスから住所録クラスを作成し、データを追加する */
fun main() {
val addressbook: Notebook<Address> = Notebook<Address>("住所録")
// 作成した住所録クラスにデータを追加していく
addressbook.pages.add(Address("hoge hoge", "hoge city", "000-0000-0000"))
addressbook.pages.add(Address("hello kotlin", "kotlin city", "111-1111-1111"))
}
# 13. 配列とコレクション
配列, コレクション早見表
配列もコレクションも知っているからいいやって方向け一覧表
タイプ | 該当クラスorインターフェース | 作成(関数名) | 要素の参照 | 要素の追加 | 要素の削除 |
---|---|---|---|---|---|
配列 | Array | arrayOf 他 | 配列[i], get(i), elementAt(i) ※ | × | × |
リスト(readonly) | List | listOf | リスト[i], get(i), elementAt(i) ※ | × | × |
リスト(readwrite) | MutableList | mutableListOf | 同上 | add, += | removeAt(i), remove(値) , -= ※ |
セット(readonly) | Set | setOf | セットの項を参照 | × | × |
セット(readwrite) | MutableSet | mutableSetOf | 同上 | add, += | remove(値), -= |
マップ(readonly) | Map | mapOf | マップ[キー], get(キー)他 | × | × |
マップ(readwrite) | MutableMap | mutableMapOf | 同上 | set, =, += | remove(キー), -= |
※ iはインデックス
配列(Array)
- 同じ型の変数が複数個連なった型
- (一般的に)ランダムアクセスが早い
- Kotlinでは
Array<T>
やIntArray
等と表現される (ByteArray,LongArray…等も存在する) - Array<Int>とIntArrayでは、IntArrayの方が速いそうです
出典: Kotlin: IntArray と Array の違い
/* 配列の作成 */
val numbers1: Array<Int> = arrayOf(1,241,61,910) // Integerオブジェクトの配列
val numbers2: IntArray = intArrayOf(1,241,61,910) // int[]
val numbers3: Array<Int?> = arrayOfNulls(4) // 要素を全てnullで初期化した配列
val numbers4 = arrayOfNulls<Int?>(4) // numbers3と同じ意味
/* 配列の参照 */
val numbers: IntArray = intArrayOf(1,241,61,910)
println(numbers[2]) // 61 と出力
println(numbers.get(2)) // 同上
println(numbers.elementAt(2)) // 同上
/* 配列要素の追加削除は無し */
リスト(List)
- データ同士をポインタで繋いだ型(詳しくは線形リストで検索)
- (一般的に)要素の挿入削除が容易
- (一般的に)配列よりシーケンシャルアクセスが遅い
- 要素の順番が保証される
- Kotlinでは
List<T>
やMutableList<T>
と表現される -
List<T>
は読み取り専用、MutableList<T>
は変更可能
/* リストの作成 */
val numbers1: List<Int> = listOf(1,241,61,910) // 読み取り専用
val numbers2: MutableList<Int> = mutableListOf(1,241,61,910) //書換可
val numbers3: List<Int> = emptyList() // 空のリスト作成
val numbers4: MutableList<Int> = mutableListOf() // 空のリスト作成
/* 要素の参照 */
val numbers: List<Int> = listOf(1,241,61,910)
println(numbers[2]) // 61と出力
println(numbers.get(2)) // 同上
println(numbers.elementAt(2)) // 同上
/* 要素の追加(MutableListのみ) */
val numbers: MutableList<Int> = mutableListOf(1,241,61,910)
numbers.add(99) // 要素の追加
numbers += -20 // 演算子でも追加できる
for(i in numbers){
println(i) // 1 241 61 910 99 -20 と出力
}
/* 要素の削除(MutableListのみ) */
val numbers: MutableList<Int> = mutableListOf(1,241,61,910)
numbers.removeAt(2) // インデックスを指定して削除
numbers.remove(910) // 値を指定して削除
numbers.remove(15) // 該当する値は無いが例外は発生しない
numbers -= 1 // 値を指定して削除
for(i in numbers){
println(i) // 241 と出力
}
/*
* 読み取り専用でも書き換え出来てしまう例
*/
val numbers1: List<Int> = listOf(1,241,61,910) // 読み取り専用
val numbers2: MutableList<Int> = mutableListOf(1,241,61,910) //書換可
numbers1[1] = 20 // 読み取り専用なのでコンパイルエラー
numbers2[1] = 20 // これはOK
// こう書くとListでも書換出来てしまう
val numbers3: List<Int> = numbers2
numbers2[3] = 99
セット(Set)
- 要素の順番が保証されない
- 同じ値の要素を保持できない
- リストより要素の検索が速い
- Kotlinでは
Set<T>
やMutableSet<T>
と表現される -
Set<T>
は読み取り専用、MutableSet<T>
は変更可能
/* セットの作成 */
val numbers1: Set<Int> = setOf(1,241,61,910) // 読み取り専用
val numbers2: MutableSet<Int> = mutableSetOf(1,241,61,910) //書換可
val numbers3: Set<Int> = setOf(1,241,61,910,1,61) // 重複した値を省いて作成される
/* 要素の参照 */
val numbers: Set<Int> = setOf(1,241,61,910)
println(numbers.elementAt(2)) // 動作するが、要素の順番が保証されないので使い道あるかな?
// forで回して1つずつ要素を取得
for(st in numbers){
println(st)
}
// 要素の参照ではないが、値が存在するか確認するだけならcontainsメソッドが使える
numbers.contains(値) // true or falseを返却
/* 要素の追加(MutableSetのみ) */
val numbers: MutableSet<Int> = mutableSetOf(1,241,61,910)
numbers.add(99)
numbers.add(61) // 重複する値
numbers += -20 // 演算子でも追加できる
for(i in numbers){
println(i) // 1 241 61 910 99 -20 と出力
}
/* 要素の削除(MutableSetのみ) */
val numbers: MutableSet<Int> = mutableSetOf(1,241,61,910)
numbers.remove(61) // 値を指定して削除
numbers.remove(99) // 存在しない値だが例外は発生しない
numbers -= 241 // 値を指定して削除
for(i in numbers){
println(i) // 1 910 と出力
}
マップ(Map)
- 要素のキーと値をペアで保持する
- 追加した要素の順番が保証されない
- Kotlinでは
Map<T>
やMutableMap<T>
と表現される -
Map<T>
は読み取り専用、MutableMap<T>
は変更可能 - Map作成時に
withDefault
を付加した場合は、getValue
メソッドとセットで使う
/* マップの作成 */
// マップの作成は、mapOf(キー to 値[,繰り返し])となる
val numbers1: Map<Int, Int> = mapOf(0 to 1, 1 to 241, 2 to 61, 4 to 910) // 読み取り専用
val numbers2: MutableMap<Int, Int> = mutableMapOf(0 to 1, 1 to 241, 2 to 61, 4 to 910) // 書換可
/* 要素の参照 */
val numbers: Map<Int, Int> = mapOf(0 to 1, 1 to 241, 2 to 61, 4 to 910)
println(numbers[2]) // 61と出力 []内はキーを指定
println(numbers.get(2)) // 同上
println(numbers[4]) // 910
println(numbers[10]) // 存在しないキーなので null と出力
println(numbers.getValue(10)) // 存在しないキーの場合、例外を投げる
println(numbers.getOrDefault(10, -1)) // null嫌だなって場合はこちら。存在しない場合2つ目の引数を返却
/* Mapを作成する時にデフォルト値を設定することも出来るのだが… */
val numbers: Map<Int, Int> = mapOf(0 to 1, 1 to 241, 2 to 61, 4 to 910).withDefault {-99}
println(numbers[10]) // こっちはデフォルト値を見ずに、キーが無いので null と出力
println(numbers.get(10)) // こちらもnull 出力
println(numbers.getValue(10)) // こちらは -99(デフォルト値) と出力
/* 要素の追加(MutableMapのみ) */
val numbers: MutableMap<Int, Int> = mutableMapOf(0 to 1, 1 to 241, 2 to 61, 4 to 910) // 書換可
numbers[3] = 33 // []内はインデックスではなくキー。左辺でキー、右辺で値を指定。存在しないキーの場合は追加
numbers += 5 to 55 // 演算子でもいける
numbers += 1 to 111 // キーが被る場合は値を上書き
numbers.set(7,77) // メソッドで追加
for(i in numbers){
println(i) // 0=1 1=111 2=61 4=910 3=33 5=55 7=77 と出力
}
/* 要素の削除(MutableMapのみ) */
val numbers: MutableMap<Int, Int> = mutableMapOf(0 to 1, 1 to 241, 2 to 61, 4 to 910) // 書換可
numbers.remove(2) // キーを指定して削除
numbers.remove(1, 241) // キーと値両方を指定して削除
numbers.remove(15) // 該当する要素は無いが例外は発生しない
numbers.remove(0, 0) // キーと値両方が一致する要素はないので削除しない
numbers -= 4 // 演算子でも出来る。 キーを指定して削除
for(i in numbers){
println(i) // 0=1 と出力
}
コレクションのループについて
配列やリストをループする際に、インデックスもやっぱり欲しいなあという場合
/* インデックスも一緒に取得できるループ */
val numbers: IntArray = intArrayOf(1,241,61,910)
for((index, number) in numbers.withIndex()){ // indexにインデックス, numberに要素が入る
println("インデックス:${index}, 数字: ${number}")
}
Mapのループで、キーと値を分けて取得する場合
val numbers: Map<Int, Int> = mapOf(0 to 1, 1 to 241, 2 to 61, 4 to 910)
for((key, value) in numbers){ // keyにキー、valueに値が入る
println("キー: ${key}, 値: ${value}")
}
forEachでループも出来ます
(ラムダ式をまだまとめていないので、今までのサンプルコードでは使っていません)
val numbers: IntArray = intArrayOf(1,241,61,910)
numbers.forEach{println(it)} // すごくスッキリ