← 記事一覧へ

Go 学習記録(7):配列・スライス・map・リスト

Go 基礎学習の第 7 回。文法、実例、学習の過程を記録します。

Go のコレクション:配列、スライス、map、リスト

1. 配列

宣言:var name [length]type

例:var name [5]int

長さのない []T はスライスです。配列とは別の型で、基底配列の一部分を表します。

1.1. 配列の基本

fmt.Println(name) で配列の要素を出力できます。

配列の長さは固定です。

配列を反復する:

for _, value := range name{
    fmt.Prinntln(value)
}

1.2. 配列の初期化

//多种初始化方式
var name [5] int = [5]int{1, 2, 3, 4, 5}
var name = [5]int{1, 2, 3, 4, 5}
name := [5]int{1, 2, 3, 4, 5}

//只初始化部分元素
name := [3]int{2:3}
//前面的都为0,name[2] = 3

//数组的长度是不确定的
name := [...]int{1, 2, 3, 4, 5}
name := [...]int{1, 2, 3}

要素型と長さが同じ配列は同じ型なので、代入できます。要素型が比較可能なら、配列も比較できます。

1.3. 多次元配列

//基本定义方式
var name [3][4] string
name[0] = [4]string{"a", "b", "c", "d"}
name[0][0] = "a"

2. スライス

スライスは基底配列の一部分を表し、長さを変えられます。値をコピーすると、同じ記憶領域を共有することがあります。

2.1. スライスの基本

2.1.1. 宣言する

var name []string
fmt.Printf(%T, name)
//输出结果为[]string,本质是切片

2.1.2. 要素を追加する

// 使用append来增加元素,注意书写的格式
name = append(name, "a")

2.2. 初期化する

主な方法は三つです。

  1. 既存の配列をスライスする

  2. []string{} のようなリテラルを使う

  3. make を使う

// 第一种
allName := [5]string{"aaa", "bbb", "ccc", "ddd", "eee"}
nameSlice := allName[0:2] // "左闭右开,包含0,不包含2"

//第二种
allName := []string{"aaa", "bbb", "ccc", "ddd", "eee"}
//在切片上也可以进行切片创建

//第三种
//第一个参数是切片的类型,第二个参数是预分配的空间(长度)
allName := make([]string, 5)

2.3. 要素へアクセスする

allName := []string{"aaa", "bbb", "ccc", "ddd", "eee"}

sliceName := allName[start:end] //左闭右开
//如果没有end,默认是到最后一个元素
//没有start,有end,默认是从第一个元素开始到end之前的元素
//冒号一定存在

2.4. スライスと省略記号

go

append には複数の要素を渡せます。

各要素を直接渡せます。

別のスライスを省略記号で展開することもできます。配列をこの形で渡すなら、先にスライスにします。

// 直接列出元素的情况
name := []string{"aaa", "bbb", "ccc", "ddd", "eee"}
name = append(name, "fff", "ggg", "hhh")

// 通过切片加入的情况
name := []string{"aaa", "bbb", "ccc", "ddd", "eee"}
name2 := []string{"fff", "ggg", "hhh"}
name = append(name, name2...)

// 通过数组加入的情况
name := []string{"aaa", "bbb", "ccc", "ddd", "eee"}
name2 := [3]string{"fff", "ggg", "hhh"}
name = append(name, name2[:]...)

2.5. 要素の削除とコピー

2.5.1. 要素を削除する

対象の前後を append でつなぐか、新しいスライスを作る方法があります。

// 切片的删除操作
name := []string{"aaa", "bbb", "ccc", "ddd", "eee"}
// 删除一个中间的元素
newName = append(name[:2], name[3:]...)

// 删除后面的元素
newName := name[:3]
newName = append(name[:3])

2.5.2. 要素をコピーする

データのコピーには複数の方法があります。

name := []string{"aaa", "bbb", "ccc", "ddd", "eee"}

// 第一种切片赋值
name2 := name[:]

// 第二种使用copy函数
var name3 = make([]string, len(name))
copy(name3, name)

記憶領域を共有するかどうかが異なります。

例: go

単なる代入では基底配列を共有するため、要素の変更が両方に見えます。別に確保した領域へ copy すると、共有を避けられます。

代入はポインタを含む記述子をコピーします。copy は宛先へ要素をコピーしますが、宛先の記憶領域を自動では確保しません。

2.6. スライスの内部

関数へ渡すときは、値渡しか参照渡しか。

値渡しです。ただし、基底配列の共有によって参照に似た動作をします。

スライスの記述子は、基底配列へのポインタ、長さ、容量を持ちます。

2.6.1. 記述子の構成

// slice的底层结构如下:
type slice struct {
    array unsafe.Pointer   //用来存储实际数据的数组指针只想一块连续的内存
    len int                //切片中元素的数量
    cap int                //array数组的长度
}

2.6.2. 容量の拡張

容量を超えて append すると、新しい配列を確保して既存の要素をコピーします。

go

増加の仕方はランタイムの版、要素の大きさ、現在の容量によって異なります。必ず 2 倍になるという前提は置きません。

2.6.3. 関数への受け渡し

記述子全体がコピーされるので、コピーされたポインタは最初、同じ基底配列を指します。

再確保が必要な append は、新しい領域を指す記述子を返します。関数内のポインタ、長さ、容量は変わりますが、呼び出し側の記述子は自動では変わりません。

共有している要素は変更できますが、呼び出し側の長さや容量は変わりません。再確保後の新しい配列への変更は、古い配列には反映されません。 {: .prompt-tip }

例:

go

go

3、map

map は順序のないキーと値の集合で、通常は期待 O(1) の検索を行います。

キーは一意で、値は重複できます。

キーには比較可能な型が必要です。真偽値、数値、文字列、ポインタ、channel、比較可能な配列や構造体などを使えます。スライス、map、関数はキーにできません。値には任意の型を使えます。

3.1. map の基本

例:

var courseMap map[string]string{
    "courseName": "golang",
    "courseType": "backend",
}
// 其中基本格式是map[key_type] value_type

3.1.1. map を初期化する

書き込む前に初期化します。nil の map は読み取れますが、代入すると panic になります。

リテラルか make で、書き込み可能な領域を作ります。

// 第一种初始化方式
var courseMap map[string]string{
    "courseName": "golang",
    "courseType": "backend",
}

// 第二种初始化方式
var courseMap = make(map[string] string, 10)//make是一个内置函数
coueseMap["courseName"] = "golang"

一方、nil のスライスには append を使えます。

3.1.2. 値を取得する

キーで値を検索します。

存在しないキーでは、値の型のゼロ値が返ります。

var courseMap map[string]string{
    "courseName": "golang",
    "courseType": "backend",
}
fmt.Println(courseMap["courseName"])

// 第二种获取元素的方式
value, ok := courseMap["courseName"]
if ok{
    fmt.Println(value)
}else{
    fmt.Println("key不存在")
}

if value, ok := courseMap["courseName"]; ok{
    fmt.Println(value)
}else{
    fmt.Println("key不存在")
}

3.1.3. 要素を削除する

組み込みの delete を使います。

存在しないキーの削除も可能です。

delete(courseMap, "courseName")
//delete(map_name, key)

3.2. map を反復する

for-range を使います。

反復の順序は未規定で、実行ごとに変わることがあります。

//第一种遍历方式,两个参数
for _, value := range courseMap{
    fmt.Println(value)
}

// 第二种遍历方式,一个参数,key,然后通过key访问value
for key := range courseMap{
    fmt.Println(key, courseMap[key])
}

4、list

4.1. リストの概要

container/list は双方向連結リストです。別々のノードに保存し、対象のノードが分かっていれば挿入や削除が容易です。

前後へのポインタを保存する分、メモリ使用量が増えます。

任意の要素を探すには走査が必要で、通常 O(n) です。

4.2. リストの基本

var mylist list.List
mylist.PushBack("go")
mylist.PushBack("java")
mylist.PushBack("python")

// 遍历list(正向)
for i := mylist.Front(); i!= nil; i = i.Next(){
    fmt.Println(i.Value)
}

// 反向遍历
for i:= mtlist.Back(); i != nil; i = i.Prev(){
    fmt.Println(i.Value)
}

mylist.PushFront("c++")//头部插入元素
mylist.PushBack("c")//尾部插入元素
mylist.InsertAfter("c#", mylist.Front())//在指定元素后面插入元素
mylist.InsertBefore("c#", mylist.Back())//在指定元素前面插入元素

mylist.Remove(mylist.Front())//删除头部元素
mylist.Remove(mylist.Back())//删除尾部元素\
mylist.Remove(i))//删除指定元素
この記事の日付について

旧サイトに記載された日付を保持しています。実際に書き始めた時期は、これより早い可能性があります。

同じシリーズ · Go の基礎学習

前の記事Go 学習記録(6):条件分岐とループ ←次の記事Go 学習記録(8):関数・defer・エラー →