Dragon Arrow written by Tatsuya Nakaji, all rights reserved animated-dragon-image-0164

go言語 配列 スライス マップ

updated on 2020-10-25


配列

配列

  • 同じ型の値を集めて並べたもの
    • 要素の型はすべて同じ
    • 要素数が違えば別の型
    • 要素数は変更できない
    • 型は型リテラルで記述することが多い


配列の宣言 3パターン

① var 変数名 [長さ]型
② var 変数名 [長さ]型 = [大きさ]型{初期値1, 初期値n} 
③ 変数名 := [...]型{初期値1, 初期値n}


package main
import "fmt"
func main(){
    // パターン①
    var arr[2] string 

        arr[0] = "Golange"
        arr[1] = "Java"

    // パターン②   
        var arr[2] string = [2]string {"Golang", "Java"}

    // パターン③
        arr := [...] string{"Golang", "Java"}


       fmt.Println(arr[0], arr[1]) //=> Golange Java
}


配列の初期化

// ゼロ値で初期化
var ns1 [5]int
// 配列リテラルで初期化
var ns2 = [5]int{10, 20, 30, 40, 50}
// 要素数を値から推論
ns3 := [...]int{10, 20, 30, 40, 50}
// 5番目が50、10番目が100で他が0の要素数11の配列
ns4 := [...]int{5: 50, 10: 100}


配列の操作


package main

import "fmt"

func main(){
   ns := [...]int{10, 20, 30, 40, 50}
   // 要素にアクセス
   fmt.Println(ns[3]) // インデックス3番目
   // 長さ
   fmt.Println(len(ns))
   // スライス演算
   fmt.Println(ns[1:4]) // インデックス1~3番目
}

40 

[20 30 40]


スライス

スライス

  • 配列の一部を切り出したデータ構造
    • 要素の型はすべて同じ
    • 要素数は型情報に含まない ( []の中にサイズを指定しない )
    • 背後に配列が存在する


Goの Arrays(配列)は固定長の様な配列である一方、Goの Slices(スライス) は可変長の配列の様な動きをするのでより柔軟にデータ(要素)を格納することが可能


配列の宣言 3パターン

① var 変数名 []型
② var 変数名 []型 = []型{初期値1, ..., 初期値n} 
③ 変数名 := 配列[start:end] //配列(またはスライス)のstartから(end - 1)を取り出す事でスライスを作成する。 


var slice []string
②
slice := [] string{"Golang", "Java"}
③
arr := [...] string{"Golang", "Java"}
slice := arr[0:2]


スライスの初期化

package main
import "fmt"
func main() {
    // ゼロ値はnil
    var ns1 []int
    fmt.Println(ns1)
    // 長さと容量を指定して初期化
    // ゼロ値で初期化
    ns1 = make([]int, 3, 10)
    fmt.Println(ns1)
    // スライスリテラルで初期化
    // 要素数は指定しなくてよい
    // 自動で配列は作られる
    var ns2 = []int{10, 20, 30, 40, 50}
    fmt.Println(ns2)
    // 5番目が50、10番目が100で他が0の要素数11のスライス
    ns3 := []int{5: 50, 10: 100}
    fmt.Println(ns3)
}

(コンソール出力)

[] 

[0 0 0] 

[10 20 30 40 50] 

[0 0 0 0 0 50 0 0 0 0 100]



スライスの操作

操作意味
Slice[start:end]start から end - 1 まで
Slice[start:]start から最後尾まで
Slice[:end]先頭から end - 1 まで
Slice[:]先頭から最後尾まで


package main

import "fmt"

func main() {
    ns := []int{10, 20, 30, 40, 50}
    n, m := 2, 4


    // インデックス2番目からスライスを取得する
    fmt.Println(ns[n:]) // [30 40 50]

    // 先頭からインデックス3番目までのスライスを取得する
    fmt.Println(ns[:m]) // [10 20 30 40]

    // capを指定する
    ms := ns[:m:m]
    fmt.Println(cap(ms)) // 4
}


 スライスの注意点


スライス操作を行う場合は生成元の配列(またはスライス)と要素を共有します。
つまり、スライスの要素を変更すると、その元となる配列の対応する要素が変更されます

func main(){
     arr := [...] string{"Golang", "Java"}
     slice := arr[0:2] //スライスの作成


     slice[0] = "Ruby" //slice[0]の要素を変更
     fmt.Println(slice) //[Ruby Java]
     fmt.Println(arr) // arrも[Ruby Java]に変更されている
}

(コンソール出力)

[Ruby Java] 

[Ruby Java]


スライスの操作


package main

import "fmt"

func main(){
     ns := []int{10, 20, 30, 40, 50, 60}
// 長さ
    fmt.Printf("サイズ => %d\n", len(ns))
    fmt.Printf("要素追加前のcapacity => %d\n", cap(ns))
// 要素の追加
// 容量が足りない場合は背後の配列が再確保される
    ns = append(ns, 70, 80)
// 容量
    fmt.Printf("要素追加後のcapacity => %d\n", cap(ns))
}


要素の追加

Goでは配列は宣言時に要素の数が固定されるので要素の追加はできない。スライスの場合は追加可能。
スライスに要素を追加したい場合は組み込み関数 append() を使用する事で簡単に行える。

appendの挙動

  • 容量が足りる場合
    • 新しい要素をコピーする
    • lenを更新する
  • 容量が足りない場合
    • 元のおよそ2倍の容量の配列を確保しなおす
    • 配列へのポインタを貼り直す
    • 元の配列から要素をコピーする
    • 新しい要素をコピーする
    • lenとcapを更新する


package main

import "fmt"

func main() {
    a := []int{10, 20}
    fmt.Println(a, cap(a)) // [10 20] 2

    b := append(a, 30)
    a[0] = 100
    fmt.Println(a, cap(a)) // [100 20] 2
    fmt.Println(b, cap(b)) // [10 20 30] 4

    c := append(b, 40)
    b[1] = 200
    fmt.Println(c, cap(c)) // [10 200 30 40] 4
}


スライス演算

package main


import "fmt"


func main() {
    ns := []int{10, 20, 30, 40, 50}
    n, m := 2, 4


    // n番目以降のスライスを取得する
    fmt.Println(ns[n:]) // [30 40 50]


    // 先頭からm-1番目までのスライスを取得する
    fmt.Println(ns[:m]) // [10 20 30 40]


    // capを指定する
    ms := ns[:m:m]
    fmt.Println(cap(ms)) // 4
}


要素の削除

  • i~j番目を削除
a = append(a[:i], a[j+1:]...)

aのi-1番目までのスライスに、j+1番目以降を追加下スライスを取得

=> i~j番目がカットされる

(例)

package main
import "fmt"

func main(){
    a := []int{10, 20, 30, 40, 50, 60}
    i, j := 1, 3
    a = append(a[:i], a[j+1:]...)
    fmt.Printf("a: %v\n", a) // a: [10 40 50 60]
}


  • i番目を削除
a = append(a[:i], a[i+1:]...)
// or
a = a[:i+copy(a[i:], a[i+1:])]

(例)

package main
import "fmt"

func main(){
    a := []int{10, 20, 30, 40, 50, 60}
    i := 2

    // 方法①
    //fmt.Println(copy(a[i:], a[i+1:]), a) // 3(コピーした要素の数) [10 20 40 50 60 60](スライスaの値)
    a = a[:i+copy(a[i:], a[i+1:])]
    //

    // 方法②
    // a = append(a[:i], a[i+1:]...)
    //

    fmt.Printf("a: %v\n", a) // a: [10 20 40 50 60]
}


マップ


  • キーと値をマッピングさせるデータ構造
    • キーと値の型を指定する
    • キーには「==」で比較できる型しかNG


初期化

// ゼロ値はnil
var m map[string]int

// makeで初期化
m = make(map[string]int)

// 容量を指定できる
m = make(map[string]int, 10)

// リテラルで初期化
m := map[string]int{"x": 10, "y": 20}

// 空の場合
m := map[string]int{}


マップの操作


package main

func main() {
    m := map[string]int{"x": 10, "y": 20}

    // キーを指定してアクセス
    println(m["x"])

    // キーを指定して入力
    m["z"] = 30

    // 存在を確認する
    n, ok := m["z"]
    println(n, ok)

    // キーを指定して削除する
    delete(m, "z")

    // 削除されていることを確認
    n, ok = m["z"] // ゼロ値とfalseを返す
    println(n, ok)
}

(コンソール出力)

10 

30 true 

0 false


要素がコンポジット型

ひとことで表すと、複数のデータ型を1つのデータ型にまとめてものである。
Go言語における具体的なコンポジット型を以下にまとめた。

種類説明
配列同じ型のデータを並べたデータ型
スライス可変長な配列
マップキーと値を連結させたデータ型
構造体異なるデータ型を集めたデータ型


  • コンポジット型を要素として持つコンポジット型
    • スライスの要素がスライスの場合(2次元スライス)
      • 例:[][]int
    • マップの値がスライスの場合
      • 例:map[string][]int
    • 構造体のフィールドの型が構造体
      • 例:
      • struct {

            A struct {

                N int

            }

        }


ユーザー定義型

typeで名前を付けて新しい型を定義する

type 型名 基底型


// 組み込み型を基にする
type MyInt int
// 他のパッケージの型を基にする
type MyWriter io.Writer
// 型リテラルを基にする
type Person struct {
     Name string
}


ユーザー定義型の特徴

  • 基底型とユーザ定義型の相互キャストが可能
type MyInt int
var n int = 100
m := MyInt(n)
n = int(m)


型のエイリアス

  • 型のエイリアスを定義できる
    • 完全に同じ型
    • キャスト不要
    • エイリアスの方ではメソッド定義はできない
type Applicant = http.Client
  • 型名を出力する%Tが同じ元の型名を出す
type Applicant = http.Client
func main() {
   fmt.Printf("%T", Applicant{})
}