【Go言語】新卒がまとめるスライスについて

## この記事を書いた目的
qiitaの記事でスライスについての記事を眺めていて
sliceってなんぞや！
記事を見てもちょっと分からないということで
[こちら](http://qiita.com/mizukmb/items/b543f88bc37c9a75673f)を実際に解説しながら
capとlen、スライスについて
説明してみました。

そして自分は新卒プログラマーでGo歴1ヶ月目なので指摘等あれば幸いです。

## ⭐️スライスとは？ 
可変長配列を持たない代わりに実装された型。
配列全体の**ポインタ(ptr)**、**配列の長さ(len)**、**容量(cap)**からなるデータ構造を保持している。

### ●型の宣言
```
var hoge []int
var hoge []int{1, 2, 3}
hoge := []int{1, 2, 3}
```

### ●動作確認
```
# 【slice.go】 #
package main

import (
    "fmt"
)

func main() {

    a := [5]string{"ドラえもん", "のび太", "しずかちゃん", "ジャイアン", "スネ夫"}
    b := a[:]
    c := a[2:4]

    fmt.Println(a)
    fmt.Println(b)
    fmt.Println(c)

    b[1] = "ドラミ"
    c[0] = "出木杉"

    fmt.Println(a)
    fmt.Println(b)
    fmt.Println(c)
}
```
```
# 【出力結果】 #
[ドラえもん のび太 しずかちゃん ジャイアン スネ夫]
[ドラえもん のび太 しずかちゃん ジャイアン スネ夫]
[しずかちゃん ジャイアン]
[ドラえもん ドラミ 出木杉 ジャイアン スネ夫]
[ドラえもん ドラミ 出木杉 ジャイアン スネ夫]
[出木杉 ジャイアン]
```
### 💡解説
まず取り出し方について事前解説

| 例 | 解説 |
|:----:|:----:|
| a[:] | 全要素 |
| a[2:] | 2番目後から最後まで |
| a[2:4] | 2番目後から4番目まで |
| a[:5] | 初めから5番目まで |



`a := [5]string{}`
まず、aという配列に
- ドラえもん
- のび太
- しずかちゃん
- ジャイアン
- スネ夫
の５人が入りました

`b := a[:]`
bはaの全取り出しなので
- ドラえもん
- のび太
- しずかちゃん
- ジャイアン
- スネ夫
aと同じように表示されます

`c := a[2:4]`
cはaの2番目後から４番目を取り出しているので
- しずかちゃん
- ジャイアン
が表示

ここまでは普通に理解できますね。

```
b[1] = "ドラミ"
c[0] = "出木杉"
```
をすることによってなぜ
表示結果がaにも反映されるのか？


それはスライスに渡した部分列の要素は
**元の配列**の**メモリ領域**が**共有**されている為。
スライス型で要素を代入するということは
**元の配列データに代入する**ということ。

よって
上記のコード

```
b[1] = "ドラミ"
c[0] = "出木杉"
```

によって`b`と`c`に代入すると
`a`にも反映されるということである。

## ⭐️組み込み関数
スライスでは組み込み関数として
- make()
- append()


が使える。


### ⭐️make関数
make(型, 要素数, 容量)
容量を省略すると、**要素数==容量**となる

#### ●動作確認
```
# 【make.go】 #
package main

import (
    "fmt"
)

func main() {
    var a []int
    a = make([]int, 5, 10)
    fmt.Println(a)
    fmt.Println(len(a))
    fmt.Println(cap(a))
    for i, _ := range a {
        a[i] = i
    }
    fmt.Println(a)

    b := make([]int, 10)
    fmt.Println(b)
    fmt.Println(len(b))
    fmt.Println(cap(b))
}
```
```
# 【出力結果】 #
[0 0 0 0 0]
5
10
[0 1 2 3 4]
[0 0 0 0 0 0 0 0 0 0]
10
10
```
#### 💡解説
make関数は
make(型, 要素数, 容量)で表現します
また、容量を省略すると**要素数==容量**となります。
上記でも述べている通りです。

#### [a]

```
a = make([]int, 5, 10)
```

型(int)
要素数(5)
容量(10)
ということが分かります。
なので表示は

```
[0 0 0 0 0]     //(a)
5               //len(a)
10              //cap(a)
```

#### [b]

```
b := make([]int, 10)
```

容量を省略しているので
型(int)
要素数(10)
容量(10)
ということが分かります。
なので表示は

```
[0 0 0 0 0 0 0 0 0 0]
10
10
```

### ⭐️append関数
append(スライス, データ1, データ2, ...)
指定したスライスの末尾に追加
スライスの要素数は自動で増えていく

#### ●動作確認
```
# 【append.go】 #
package main

import (
  "fmt"
)

func main() {
  a := []string{"ドラえもん", "のび太", "しずかちゃん", "ジャイアン", "スネ夫"}
  fmt.Println(a)
  fmt.Println(len(a))
  fmt.Println(cap(a))

  a = append(a, "ミニドラ", "出木杉")
  fmt.Println(a)
  fmt.Println(len(a))
  fmt.Println(cap(a))

  a = append(a, "ジャイ子", "のび助", "玉子", "スネ吉")
  fmt.Println(a)
  fmt.Println(len(a))
  fmt.Println(cap(a))
}
```
```
# 【出力結果】 #
[ドラえもん のび太 しずかちゃん ジャイアン スネ夫]
5
5
[ドラえもん のび太 しずかちゃん ジャイアン スネ夫 ミニドラ 出木杉]
7
10
[ドラえもん のび太 しずかちゃん ジャイアン スネ夫 ミニドラ 出木杉 ジャイ子 のび助 玉子 スネ吉]
11
20
```

#### 💡解説

最大の特徴は
容量はいっぱいになった時、
現在の **<span style="color: #ef0606">容量を倍</span>** にして確保してくれます。
配列のメモリアドレスは容量の拡張に伴い変更されます。

それを踏まえて解説します。

#### [パート1]

```
【append.go】
 a := []string{"ドラえもん", "のび太", "しずかちゃん", "ジャイアン", "スネ夫"}
 fmt.Println(a)
 fmt.Println(len(a))
 fmt.Println(cap(a))
```
```
【出力】
[ドラえもん のび太 しずかちゃん ジャイアン スネ夫]
5
5
```
これであれば分かりやすいですね
aには５人のキャラクターがいるので
len(５)
cap(５)
となります

#### [パート2]
```
【append.go】
  a = append(a, "ミニドラ", "出木杉")
  fmt.Println(a)
  fmt.Println(len(a))
  fmt.Println(cap(a))
```
```
【出力】
[ドラえもん のび太 しずかちゃん ジャイアン スネ夫 ミニドラ 出木杉]
7
10
```
指定が無いので末尾に追加されているのが分かります。
<span style="color: #ef0606">cap</span>に注目してみましょう。

一番最初は
len(5)
cap(5)
であったaですが
最初のlenの長さをオーバーした為
自動で倍に容量を増やしてくれているのが分かります。

パート３も同様です。


『ではもし容量が確保されていないとどうなる？？？
容量って気にする必要ある？』

という事で実験をしてみましょう。

### 🙆容量が確保されている場合 

前提条件：要素数7,容量10

#### ●動作確認
```
# 【test1.go】 #
package main

import (
	"fmt"
)

func main() {
	a := make([]string, 7, 10)
	a[0] = "ドラえもん"
	a[1] = "のび太"
	a[2] = "しずかちゃん"
	a[3] = "ジャイアン"
	a[4] = "スネ夫"
	a[5] = "ミニドラ"
	a[6] = "出木杉"

	b := append(a[:5], "ジャイ子", "のび助")
	fmt.Println(a)
	fmt.Println(len(a))
	fmt.Println(cap(a))
	fmt.Println(b)
	fmt.Println(len(b))
	fmt.Println(cap(b))

	b = append(a[:5], "玉子", "スネ吉", "パーマン", "ジャイ子", "のび助")
	fmt.Println(a)
	fmt.Println(len(a))
	fmt.Println(cap(a))
	fmt.Println(b)
	fmt.Println(len(b))
	fmt.Println(cap(b))

	b[5] = "サザエさん"
	b[6] = "しんのすけ"
	fmt.Println(a)
	fmt.Println(len(a))
	fmt.Println(cap(a))
	fmt.Println(b)
	fmt.Println(len(b))
	fmt.Println(cap(b))
}

```
```
# 【出力結果】 #
[ドラえもん のび太 しずかちゃん ジャイアン スネ夫 ジャイ子 のび助]
7
10
[ドラえもん のび太 しずかちゃん ジャイアン スネ夫 ジャイ子 のび助]
7
10
[ドラえもん のび太 しずかちゃん ジャイアン スネ夫 玉子 スネ吉]
7
10
[ドラえもん のび太 しずかちゃん ジャイアン スネ夫 玉子 スネ吉 ジャイ子 のび助]
9
10
[ドラえもん のび太 しずかちゃん ジャイアン スネ夫 サザエさん しんのすけ]
7
10
[ドラえもん のび太 しずかちゃん ジャイアン スネ夫 サザエさん しんのすけ ジャイ子 のび助]
9
10
```

#### 💡解説

#### [パート1]

```
【コード】
b := append(a[:5], "ジャイ子", "のび助")
fmt.Println(a)
fmt.Println(len(a))
fmt.Println(cap(a))
fmt.Println(b)
fmt.Println(len(b))
fmt.Println(cap(b))

【出力】
[ドラえもん のび太 しずかちゃん ジャイアン スネ夫 ジャイ子 のび助]
7
10

[ドラえもん のび太 しずかちゃん ジャイアン スネ夫 ジャイ子 のび助]
7
10
```
まずは

```
b := append(a[:5], "ジャイ子", "のび助")
```

この一文が入りますので
bにはaの5番目までとジャイ子+のび助が入ります。

そして
出力結果ですが
上記でも述べているように

>スライスに渡した部分列の要素は
>**元の配列**の**<span style="color: #ef0606">メモリ領域</span>**が**共有**されている為。

>スライス型で要素を代入するということは
>**元の配列データに代入する**ということ。

の為bに入れた値はaにも反映される為
出力が同じになっています

#### [パート2]

```
【コード】
b = append(a[:5], "玉子", "スネ吉", "パーマン", "ジャイ子", "のび助")
fmt.Println(a)
fmt.Println(len(a))
fmt.Println(cap(a))
fmt.Println(b)
fmt.Println(len(b))
fmt.Println(cap(b))

【出力】
[ドラえもん のび太 しずかちゃん ジャイアン スネ夫 玉子 スネ吉]
7
10

[ドラえもん のび太 しずかちゃん ジャイアン スネ夫 玉子 スネ吉 ジャイ子 のび助]
9
10
```

まずは

```
b = append(a[:5], "玉子", "スネ吉", "パーマン", "ジャイ子", "のび助")
```

この一文にてbはaの5番目までと
玉子+スネ吉+パーマン+ジャイ子+のび助が入ります
つまりbは
ドラえもん のび太 しずかちゃん ジャイアン スネ夫 玉子 スネ吉 ジャイ子 のび助
になります。

#### [パート3]

```
【コード】
b[5] = "サザエさん"
b[6] = "しんのすけ"
fmt.Println(a)
fmt.Println(len(a))
fmt.Println(cap(a))
fmt.Println(b)
fmt.Println(len(b))
fmt.Println(cap(b))

【出力】
[ドラえもん のび太 しずかちゃん ジャイアン スネ夫 サザエさん しんのすけ]
7
10

[ドラえもん のび太 しずかちゃん ジャイアン スネ夫 サザエさん しんのすけ ジャイ子 のび助]
9
10

```

まずは

```
b[5] = "サザエさん"
b[6] = "しんのすけ"
```

これはもう分かりますね。

ここで言いたいのは
配列のメモリを共有しているので、
aとbは同じデータ構造であり
容量に変化はないため
確保されている要素では共有状態にあると言える。ということです。

十分な容量を確保せずにappendした場合
自動的に容量を倍にする為、配列のメモリアドレスが変更されてしまいます。

そうなってしまうと
aとbは独立されたスライスとなってしまい
データの編集・追加をしても
共有されることはなくなってしまいます。

実例をみてみましょう

先ほどのコードを3文字消しただけです
(容量部分を消しただけ)

### 🙅容量が確保されていない場合 
前提条件：要素数7,容量7

#### ●動作確認
```
# 【test2.go】 #
package main

import (
	"fmt"
)

func main() {
	a := make([]string, 7)
	a[0] = "ドラえもん"
	a[1] = "のび太"
	a[2] = "しずかちゃん"
	a[3] = "ジャイアン"
	a[4] = "スネ夫"
	a[5] = "ミニドラ"
	a[6] = "出木杉"

	b := append(a[:5], "ジャイ子", "のび助")
	fmt.Println(a)
	fmt.Println(len(a))
	fmt.Println(cap(a))
	fmt.Println(b)
	fmt.Println(len(b))
	fmt.Println(cap(b))

	b = append(a[:5], "玉子", "スネ吉", "ジャイ子", "のび助")
	fmt.Println(a)
	fmt.Println(len(a))
	fmt.Println(cap(a))
	fmt.Println(b)
	fmt.Println(len(b))
	fmt.Println(cap(b))

	b[5] = "サザエさん"
	b[6] = "しんのすけ"
	fmt.Println(a)
	fmt.Println(len(a))
	fmt.Println(cap(a))
	fmt.Println(b)
	fmt.Println(len(b))
	fmt.Println(cap(b))
}

```

```
# 【出力結果】 #
[ドラえもん のび太 しずかちゃん ジャイアン スネ夫 ジャイ子 のび助]
7
7
[ドラえもん のび太 しずかちゃん ジャイアン スネ夫 ジャイ子 のび助]
7
7
[ドラえもん のび太 しずかちゃん ジャイアン スネ夫 ジャイ子 のび助]
7
7
[ドラえもん のび太 しずかちゃん ジャイアン スネ夫 玉子 スネ吉 ジャイ子 のび助]
9
14
[ドラえもん のび太 しずかちゃん ジャイアン スネ夫 ジャイ子 のび助]
7
7
[ドラえもん のび太 しずかちゃん ジャイアン スネ夫 サザエさん しんのすけ ジャイ子 のび助]
9
14
```

このようにそれぞれが独立したスライスになって
sliceの強みが無くなってしまうのであれば
コピーでいいじゃんということになってしまいます。


## 参考
・[Go言語 スライス確認](http://qiita.com/mizukmb/items/b543f88bc37c9a75673f)
・[Go by Example: Slices](https://gobyexample.com/slices)