27
Help us understand the problem. What are the problem?

More than 3 years have passed since last update.

posted at

updated at

[Golang] 配列とスライスの違い

初めに

Golangの勉強を始めて配列とスライスの違い、make()を使用して作成したスライスとの違いがよくわからなかったため、配列とスライスの違いを調べてみました。

Golangには対話モードはありませんが、見やすくするために記事中のコードは対話モードのように表示しています。

定義方法

配列の定義方法

array0 := [3]int {1, 2, 3}
array1 := [...]int {1, 2, 3, 4}

サイズを指定すると配列が生成されます。

スライスの定義方法

slice0 := []int {1, 2, 3}
slice1 := make([]int, 3, 5)

サイズ未指定、またはmake()を使用するとスライスが生成されます。

appendによる要素の追加

配列への要素追加

array := [3]int{1, 2, 3}
array = append(array, 4)

>> .\append_panic.go:7:17: first argument to append must be slice; have [3]int

panicが発生してしまいました。配列は固定長なので要素の追加ができません。

スライスへの要素追加

slice := []int{1, 2, 3}
slice = append(slice, 4)

>> slice: [1 2 3 4]

スライスは可変長なので、append()による要素の追加はできます。

変数への代入

配列、スライスを作成後に変数に代入したときの挙動を見てみます。

配列の変数への代入

配列作成、代入

array0 := [...]int{1, 2, 3, 4}
array1 := array0

>> array0: [1 2 3 4]
>> array1: [1 2 3 4]
>> &array0[0]: 0xc00000a360
>> &array1[0]: 0xc00000a380

配列の変数への代入は値のコピーしますが、アドレスが異なっていることから実態は別物です。

値変更

array0[0] = 5

>> array0: [5 2 3 4]
>> array1: [1 2 3 4]

代入先とは別物になっているので、値を変更しても代入先は反映されません。

スライスの変数への代入

スライス作成、代入

slice0 := []int{1,2,3,4}
slice1 := slice0

>> slice0: [1 2 3 4]
>> slice1: [1 2 3 4]
>> &slice0[0]: 0xc000058100
>> &slice1[0]: 0xc000058100

スライスは作成時に裏側で配列を作成し、スライス自体はその配列を持つインスタンスになります。
スライス自体を代入すればスライス自身の参照がコピーされます。そのため代入先も同じ配列を参照できます。

値変更

slice0[0] = 5

>> slice0: [5 2 3 4]
>> slice1: [5 2 3 4]

slice0、slice1は同じものを参照しているため、値を変更すればどちらからでも値の変更が確認できます。

appendによる要素追加

slice2 := append(slice0, 5)

>> slice0: [5 2 3 4]
>> slice1: [5 2 3 4]
>> slice2: [5 2 3 4 5]
>> &slice0[0]: 0xc000058100
>> &slice1[0]: 0xc000058100
>> &slice2[0]: 0xc00008a100

スライスへの要素の追加をappend()で行うと、スライスが返却されます。これは新しく作成された別のスライス(裏側の配列も作成される)になります。
そのため、slice0 = append(slice0, 1)のように元の変数に代入してしまうと、先ほど代入したスライスとは別物となるので注意する必要があります。

slice0[1] = 6
slice2[2] = 6

>> slice0: [5 6 3 4]
>> slice1: [5 6 3 4]
>> slice2: [5 2 6 4 5]

要素追加後の値の変更も、別物なのでこのようになります。

makeで作成したスライスの変数への代入

makeでスライス作成、代入

slice0 := make([]int, 5, 6)
slice1 := slice0

>> slice0:[0 0 0 0 0], len:5, cap:6
>> slice1:[0 0 0 0 0], len:5, cap:6
>> &slice0[0]: 0xc000080090
>> &slice1[0]: 0xc000080090

makeでスライス作成しました。make()は第一引数がデータ型(スライス、マップ、チャネルのみ)、第二引数がサイズ、第三引数が容量になります。
makeでスライス作成後に代入を行いましたが、makeで作成しない場合と同じ挙動になります。

スライスへの追加(capacity内)

slice2 := append(slice0, 1)

>> slice0:[0 0 0 0 0], len:5, cap:6
>> slice1:[0 0 0 0 0], len:5, cap:6
>> slice2:[0 0 0 0 0 1], len:6, cap:6
>> &slice0[0]: 0xc000080090
>> &slice1[0]: 0xc000080090
>> &slice2[0]: 0xc000080090

append()を使用して容量を超えないスライスへの要素の追加をしました。この場合には返却されるスライスは、アドレスが同じことからも拡張前と同じスライスになります。

スライスへの追加(capacity超過)

slice3 := append(slice2, 2)

>> slice0:[0 0 0 0 0], len:5, cap:6
>> slice1:[0 0 0 0 0], len:5, cap:6
>> slice2:[0 0 0 0 0 1], len:6, cap:6
>> slice3:[0 0 0 0 0 1 2], len:7, cap:12
>> &slice0[0]: 0xc000080090
>> &slice1[0]: 0xc000080090
>> &slice2[0]: 0xc000080090
>> &slice3[0]: 0xc00004c060

次にappend()を使用して容量を超えるスライスへの追加をしました。この場合に返却されるスライスは、容量が拡張された新しいスライスが返却されました。

make()を使用しないスライスの要素追加と同じ挙動になりました。make()を使用しないスライスは最初はサイズと容量が同じなので、append()で要素を追加するには容量が足りないため、容量を拡張する必要があるので新しいスライスが作られます。

スライスのコピー

スライスを配列のようにコピーしたい場合はcopy()を使用すると、配列の変数への代入と同じように別のスライスが作成されます。
copy()を使用する注意点としては、コピー先のスライスのサイズをコピー元に合わせないとエラーになってしまう点です。

slice0 := []int{1, 2, 3}
slice1 := make([]int, len(slice0))
copy(slice0, slice1)

>> slice0:  [1 2 3]
>> slice1:  [1 2 3]
>> &slice0[0]:  0xc00000a320
>> &slice1[0]:  0xc00000a340

配列からスライスを作成

以下のようにすると配列を参照するスライスが作成されます。

array := [5]int{1, 2, 3, 4, 5}
slice := array[:]

>> reflect.TypeOf(array): [5]int
>> reflect.TypeOf(slice): []int
>> arary: [1 2 3 4 5]
>> slice: [1 2 3 4 5]
>> &array[0]: 0xc0000480c0
>> &slice[0]: 0xc0000480c0

まとめ

  • 配列は固定長、スライスは可変長
  • 配列はappend()で拡張できない、スライスはできる
  • 配列は変数への代入でコピーが作れる、スライスはcopy()を使用しないと作れない
  • スライスの変数への代入後の取り扱いには注意する必要あり
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Sign upLogin
27
Help us understand the problem. What are the problem?