Kotlinコレクション入門

## 自己紹介
<img src="https://qiita-image-store.s3.amazonaws.com/0/28302/63d28421-104b-3066-2712-768e6aded5f7.jpeg" width="200px" class="pull-right"/>

- opengl-8080
- 主に Qiita で技術メモを書いたり
- 趣味で Kotlin, Java 8, Java EE をさわり  
仕事で Excel VBA, Java 6, Struts1 を使う関西の SE
- 関西（たまに東京）の勉強会に出没

---

## Kotlin といえば？

---
## 便利な機能の話はよく聞くが...

- Nullable
- Non-Null Type
- 関数リテラル
- 拡張関数
- 演算子オーバーロード
- スマートキャスト
- クラス委譲
- プロパティ委譲
- データクラス
- etc...

---

## コレクションの話を<br>あまり聞かない<span class="h3">気がする</span><br><span class="h3">自分の調べ方が甘いだけかも...</span>

---

## いざコレクションを<br>使おうとすると...

---

- Java の `ArrayList` を使えばいいの？
    - Kotlin のコレクションがあるらしい
    - どういうクラスがあるの？
    - クラス間の関係は？
    - Java のコレクションとの変換は？
- どうやってインスタンス作るの？
    - ファクトリ関数みたいなのがあるらしい
    - 何種類あるの？
- どういうメソッドが用意されているの？
    - Stream API でやってたアレ、<br>Kotlin のコレクションではどうやるの？
- 型引数で `<? extends Hoge>` とかしてたの、<br>どうすればいいの？

---

## (；´・ω・)<br><span class="small">結構分からないことが多い</span>

---

## 言語を覚えるために勉強すること
- 開発環境の構築方法
- 文法（変数宣言、関数宣言、クラス宣言、制御文、etc...）
- **コレクションの使い方**

---

## コレクションは、<br>言語を学ぶうえで文法と<br>同じくらい基本的なもの

---

## Kotlinコレクション入門

---
## 対象者のイメージ
- Kotlin はじめたばかり
- Kotlin 使ってるけど、そういえばコレクションについて詳しく調べたことない

---

## Kotlin のコレクションには<br>どんなクラスがある？

---

## Kotlin のコレクション

<div class="text-center">
<img src="https://qiita-image-store.s3.amazonaws.com/0/28302/8fb818d2-3fa2-78f6-8357-770eab508f3c.png" height="400px" />
</div>

---

## Kotlin のコレクション
- `List`, `Set`, `Map` の３つ
    - `List`：順序を持つコレクション。重複可
    - `Set`：順序を持たないコレクション。重複不可
    - `Map`：キーバリューで値を保持するコレクション

---

## Java との違い

<div class="text-center">
<img src="https://qiita-image-store.s3.amazonaws.com/0/28302/e6336a40-34e7-ca3c-e0d4-5ccdd7d9cfae.png" height="400px" />
</div>

---

## 読み取り専用とミュータブル
- `List`, `Set`, `Map` は読み取り専用
    - `add()` などの更新系メソッドを持っていない
- 更新系のメソッドは、 `Mutable` が頭についた  
インターフェースに定義されている

---

## 読み取り専用 ≠ イミュータブル
```kotlin
val mutableList = mutableListOf(1, 2, 3)
val readonlyList: List<Int> = mutableList

mutableList.add(9)
println(readonlyList)
```

```text:実行結果
[1, 2, 3, 9]
```

---

## どうやってインスタンスを作る？

---

## インスタンスの作り方
```kotlin
val list = listOf(1, 2, 3)
val set = setOf(1, 2, 3)
val map = mapOf(1 to 10, 2 to 20, 3 to 30)
```

- `～Of()` という関数が用意されている
- それぞれ引数が可変長になっている

---

## インスタンスの作り方
```kotlin
val mutableList = mutableListOf(1, 2, 3)
val mutableSet = mutableSetOf(1, 2, 3)
val mutableMap = mutableMapOf(1 to 10, 2 to 20, 3 to 30)
```

- ミュータブルなコレクションをつくる場合は、  
`mutable～Of()` 関数を使用する

---

## Java コレクションのインスタンスを作る

```kotlin
val arrayList = arrayListOf(1, 2, 3)
val hashSet = hashSetOf(1, 2, 3)
val hashMap = hashMapOf(1 to 10, 2 to 20, 3 to 30)
```

- `ArrayList` や `HashMap` など、 Java のコレクションのインスタンスを作る関数も用意されている
- `linkedMapOf()`, `sortedSetOf()` などもある

---

## Java のコレクションとの相互変換はどうする？

---

## Java コレクションとの相互変換

- Kotlin には、 Java の型と相互に自動変換する仕組みが用意されている
    - Java の `int` ⇔ Kotlin の `kotlin.Int`
    - Java の `java.lang.String` ⇔ Kotlin の `kotlin.String`

---

## Java コレクションとの相互変換

- コレクションも自動変換の対象
    - `java.util.Iterable` ⇔ `kotlin.collections.Iterable`
    - `java.util.Collection`  
⇔ `kotlin.collections.Collection`
    - `java.util.List` ⇔ `kotlin.collections.List`
    - `java.util.Set` ⇔ `kotlin.collections.Set`
    - `java.util.Map` ⇔ `kotlin.collections.Map`

---

## 変換のための特別な実装は不要

```java:Javaのコード
public class Foo {
    public static java.util.List<java.lang.String>
        getList() {...}
}
```

```kotlin:Kotlinのコード
val list: kotlin.collections.List<kotlin.String>
        = Foo.getList();
```

---

## Kotlin のコレクションでは、<br>どんなメソッドが利用できる？

---

## コレクションで使えるメソッド
- 演算子オーバーロードを利用した簡易な記法
- Stream API のような内部イテレーション用のメソッド

---

## 演算子オーバーロード

```kotlin
// +, -
val from = listOf(1, 2, 3)
val to = from + 9 - 2

println("from=${from}, to=${to}")

// +=, -=
val mutableList = mutableListOf(1, 2, 3)
mutableList += 7
mutableList -= 1

println("mutableList=${mutableList}")

// [n]
println("mutableList[1] = ${mutableList[1]}")
```

```text:実行結果
from=[1, 2, 3], to=[1, 3, 9]
mutableList=[2, 3, 7]
mutableList[1] = 3
```

- `+`, `-` は、内部の状態を変更せずに、新しいコレクションを返す。
- `+=`, `-=` は、内部の状態を変更する。
- `[]` で各要素にアクセス可能（set も可能）。

---

## 内部イテレーション用のメソッド
```kotlin
val list = listOf(1, 2, 3)

list.forEach { println("forEach : " + it) }

println("all : " + list.all { it < 4 })

println("any : " + list.any { it == 2 })

println("filter : " + list.filter { it%2 == 1 })

println("map : " + list.map { it * 10 })

println("joinToString : " + list.joinToString(" > ") { "($it)" })
```

```text:実行結果
forEach : 1
forEach : 2
forEach : 3
all : true
any : true
filter : [1, 3]
map : [10, 20, 30]
joinToString : (1) > (2) > (3)
```

- 多くの内部イテレーション用のメソッドが用意されている。
- Stream API でできたことは、だいたい標準で可能。

---

## 欲しいメソッドが無い場合は？
- 例えば `peek()` に該当するメソッドは無かったぽい。
- Kotlin の拡張関数を使えば、欲しいメソッドを追加できる。

---

## peek() メソッド追加してみる
```kotlin
fun <T> Iterable<T>.peek(iterator: (T) -> Unit) : Iterable<T> {
    this.forEach(iterator)
    return this
}

fun main(args: Array<String>) {
    (0..10)
        .filter { it%2 == 0 }
        .peek { println("peek : $it") }
        .filter { it < 5 }
        .forEach { println("forEach : $it") }
}
```

```text:実行結果
peek : 0
peek : 2
peek : 4
peek : 6
peek : 8
peek : 10
forEach : 0
forEach : 2
forEach : 4
```

---

## List<? extends Hoge> <br>みたいなのはどうする？

---

## 共変
- `B` が `A` のサブタイプのときに、 `Foo<B>` が `Foo<A>` のサブタイプとなる場合は共変
- 要は、 `List<Number>` の変数に `List<Int>` の変数を代入できたら共変

---

## Java は共変ではない
```java
List<Integer> intList = new ArrayList<>();
List<Number> numberList = intList;
// コンパイルエラー！！
```

以下のように記述すれば代入できる

```java
List<Integer> intList = new ArrayList<>();
List<? extends Number> numberList = intList; // OK
```

<small>※ただし、配列は共変</small>

---

## Java は共変ではない
ただし、 `add()` などの内容を書き換えるメソッドを実行しようとするとコンパイルエラーになる

```java
List<Integer> intList = new ArrayList<>();
List<? extends Number> numberList = intList;
numberList.add(10); // コンパイルエラー！！
```

---

## なぜ書き込みできない？
```java
List<Integer> intList = new ArrayList<>();
List<? extends Number> numberList = intList;

Number number = new BigDecimal(1.1);
numberList.add(number); 
// add() を許可すると、
// numberList の実体は List<Integer> なのに、
// BigDecimal が追加できてしまう！

Integer i = intList.get(0); // BigDecimal が返ってくる！？
```

---

## Kotlin は共変？
```kotlin
val intList: List<Int> = listOf(1, 2, 3)
val numberList: List<Number> = intList
// コンパイルエラーにならない！
```

- Kotlin の `List<Int>` は、 `List<Number>` に代入できる
- ということは、 Kotlin は共変？

---

## Kotlin の List は読み取り専用

- Kotlin の `List` は読み取り専用なので `add()` などのメソッドを持っていない
- `List<Number>` に `List<Int>` を代入しても問題ない

---

## Mutableなコレクションは共変ではない

```kotlin
val intList: MutableList<Int> = mutableListOf(1, 2, 3)
val numberList: MutableList<Number> = intList
// コンパイルエラー！！
```

- `MutableList<Number>` に `MutableList<Int>` を代入しようとしたらコンパイルエラーになる

---

## コンパイラは、<br>その型が読み取り専用かどうかを<br>どうやって判断している？

---

## Kotlin のソースを見てみる
```kotlin:
public interface List<out E> : kotlin.collections.Collection<E> {
```

- 型引数が `<out E>` となっている。

---

## 読み取り専用であることを宣言する
```kotlin
class Foo<out T>(val t: T) {

    fun get() = this.t
    fun set(t: T) {}
    // Type Parameter T is declared as 'out'
    // but occurs in 'in' position in type T.
}
```

- `<out T>` とすると、 `T` はメソッドの引数など「入力」となる場所で使用できなくなる。

---

## まとめ
- Kotlin のコレクションには `List`, `Set`, `Map` がある
- コレクションは読み取り専用とミュータブルの２種類に分かれている
- ファクトリ関数を使ってインスタンスを生成する
- Java のコレクションとは相互に自動変換される
- Stream API のような内部イテレーション用のメソッドが用意されている
    - なければ拡張関数で自由に追加できる
- 読み取り専用のコレクションは共変性を持つ

---

## より詳細な情報は Qiita の記事を参照してください
- [Kotlin のコレクション使い方メモ - Qiita](http://qiita.com/opengl-8080/items/36351dca891b6d9c9687)
- [Kotlin 勉強メモ - Qiita](http://qiita.com/opengl-8080/items/d7f56f3a39cb905104db)
- [Kotlin の Extensions について色々試す - Qiita](http://qiita.com/opengl-8080/items/a7cdd746131337e7c9a6)