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. 初期化する
主な方法は三つです。
既存の配列をスライスする
[]string{} のようなリテラルを使う
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. スライスと省略記号

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)
記憶領域を共有するかどうかが異なります。
例:

単なる代入では基底配列を共有するため、要素の変更が両方に見えます。別に確保した領域へ copy すると、共有を避けられます。
代入はポインタを含む記述子をコピーします。copy は宛先へ要素をコピーしますが、宛先の記憶領域を自動では確保しません。
2.6. スライスの内部
関数へ渡すときは、値渡しか参照渡しか。
値渡しです。ただし、基底配列の共有によって参照に似た動作をします。
スライスの記述子は、基底配列へのポインタ、長さ、容量を持ちます。
2.6.1. 記述子の構成
// slice的底层结构如下:
type slice struct {
array unsafe.Pointer //用来存储实际数据的数组指针只想一块连续的内存
len int //切片中元素的数量
cap int //array数组的长度
}
2.6.2. 容量の拡張
容量を超えて append すると、新しい配列を確保して既存の要素をコピーします。

増加の仕方はランタイムの版、要素の大きさ、現在の容量によって異なります。必ず 2 倍になるという前提は置きません。
2.6.3. 関数への受け渡し
記述子全体がコピーされるので、コピーされたポインタは最初、同じ基底配列を指します。
再確保が必要な append は、新しい領域を指す記述子を返します。関数内のポインタ、長さ、容量は変わりますが、呼び出し側の記述子は自動では変わりません。
共有している要素は変更できますが、呼び出し側の長さや容量は変わりません。再確保後の新しい配列への変更は、古い配列には反映されません。 {: .prompt-tip }
例:


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))//删除指定元素