スライスとマップ
記事ディレクトリ
1. スライス
1. スライスを定義する
Go 言語のスライスは配列の抽象化です。
Go 配列の長さは変更できず、そのようなコレクションは特定のシナリオには適していません。Go は、柔軟で強力な組み込み型スライス (「動的配列」) を提供します与数组相比切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大
。
スライスは便利で柔軟かつ強力なラッパーです。スライス自体にはデータはなく、既存の配列への参照にすぎません。
配列と比較して、スライスは長さを設定する必要がありません。在 [ ] 中不用设定值,相对来说比较自由
概念的には、スライスは 3 つの要素を含む構造のようなものです。
- ポインタ: 配列内のスライスで指定された開始位置を指します。
- 長さ: スライスの長さ
- 最大長: スライスの開始位置から配列の最後の位置までの長さ
package main
import "fmt"
func main() {
// 数组的长度一旦定义就是不可改变的
arr := [4]int{
1, 2, 3, 4}
fmt.Println(arr)
// 定义切片、长度就是可变的
var c1 []int
fmt.Println(c1)
if c1 == nil {
fmt.Println("切片为空")
}
c2 := []int{
1, 2, 3, 4, 5}
fmt.Println(c2)
fmt.Printf("%T,%T\n", c1, arr) // []int,[4]int
fmt.Println(c2[0])
fmt.Println(c2[1])
}
2. make関数はスライスを作成します
- make() 関数を使用してスライスを作成する
- make( [ ] T,長さ,容量)
- スライスはインデックス付け可能で、長さは len() メソッドで取得できます。
- スライスには、スライスの容量を測定するためのメソッド cap() が用意されています。
package main
import "fmt"
func main() {
// make( [ ] T,length,capacity)
a1 := make([]int, 5, 10)
fmt.Println(a1)
fmt.Println("长度:", len(a1))
fmt.Println("容量:", cap(a1))
//容量 10 长度5
//虽然容量为10,但是可以进行操作的取决于长度5
a1[1] = 66
a1[6] = 99
fmt.Println(a1)
}
3. スライスの拡張とトラバース
package main
import "fmt"
func main() {
// 扩容
a1 := make([]int, 5, 10)
a1 = append(a1, 1, 2, 3)
fmt.Println(a1)
// 如果切片中的数据超过了规定的容量,那么他就会自动扩容
a2 := make([]int, 0, 5)
a2 = append(a2, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
fmt.Println(a2)
a3 := []int{
6, 6, 6, 6, 6, 6}
// 将切片a3追加到切片a1
a1 = append(a1, a3...)
fmt.Println(a1)
for i := 0; i < len(a1); i++ {
fmt.Println(a1[i])
}
fmt.Println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>")
for _, v := range a1 {
fmt.Println(v)
}
}
拡張メモリ解析
package main
import "fmt"
func main() {
a1 := []int{
1, 2, 3}
fmt.Println(a1)
fmt.Printf("len:%d,cap:%d\n", len(a1), cap(a1)) //len:3,cap:3
fmt.Printf("%p\n", a1)
a1 = append(a1, 4, 5, 6)
fmt.Println(a1)
fmt.Printf("len:%d,cap:%d\n", len(a1), cap(a1)) //len:6,cap:6
fmt.Printf("%p\n", a1)
a1 = append(a1, 7, 8, 9, 10)
fmt.Println(a1)
fmt.Printf("len:%d,cap:%d\n", len(a1), cap(a1)) //len:6,cap:6
fmt.Printf("%p\n", a1)
a1 = append(a1, 10, 11, 12, 13)
fmt.Println(a1)
fmt.Printf("len:%d,cap:%d\n", len(a1), cap(a1)) //len:6,cap:6
fmt.Printf("%p\n", a1)
}
- 各スライスは基礎となる配列を参照します
- スライス自体はデータを格納せず、基になる配列に格納されるため、スライスを変更することは配列内のデータを変更することを意味します。
- スライスにデータを追加する場合、容量を超えない場合は直接追加してください。容量を超えた場合は自動的に容量が拡張され、容量が2倍になります。
実装の原則は Copy メソッドを使用することです
package main
import "fmt"
func main() {
nums := []int{
1, 2, 3}
fmt.Printf("len:%d,cap:%d slice=%v\n", len(nums), cap(nums), nums) //len:3,cap:3
/* 创建切片 nums1 是之前切片的两倍容量 */
nums1 := make([]int, len(nums), (len(nums))*2)
/* 拷贝 nums 的内容到 nums1 */
copy(nums1, nums)
fmt.Printf("len:%d,cap:%d slice=%v\n", len(nums1), cap(nums1), nums1)
}
- スライスが展開されると、新しい基礎となる配列を再度ポイントします。
4. 既存のアレイ上にスライスを作成する
既存の配列から直接スライスを作成します。スライスの基になる配列は現在の配列 です长度是从 start 带 end 切割的数据量,但是容量是从start 到数组的末尾
。
package main
import "fmt"
func main() {
arr := [10]int{
1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
fmt.Println("通过数组创建切片")
// arr[start,end] 包含start:end不包含
a1 := arr[:5] // 1~5
a2 := arr[5:8] // 6~8
a3 := arr[8:10] // 8~10
a4 := arr[:] // 1~10
fmt.Println(a1)
fmt.Println(a2)
fmt.Println(a3)
fmt.Println(a4)
// 改数组中的元素,则切片中元素也会改,因为他们指向同一存储空间
arr[0] = 99
fmt.Println(arr)
fmt.Println(a1)
a1[1] = 100
fmt.Println(arr)
fmt.Println(a1)
fmt.Printf("%p\n", a1)
// arr 是一个数组,所以需要用 & 取出地址
fmt.Printf("%p\n", &arr)
fmt.Printf("len:%d,cap:%d\n", len(a1), cap(a1))
fmt.Printf("len:%d,cap:%d\n", len(a2), cap(a2))
fmt.Printf("len:%d,cap:%d\n", len(a3), cap(a3))
fmt.Printf("len:%d,cap:%d\n", len(a4), cap(a4))
// 进行扩容,如果超过了容量,则赋值生成新的一个数组
a1 = append(a1, 1, 1, 1, 1, 1, 1, 1)
fmt.Println(arr)
fmt.Println(a1)
// 切片改了数组没有发生变化
a1[0] = 1000
fmt.Println(arr)
fmt.Println(a1)
fmt.Printf("%p\n", a1)
// arr 是一个数组,所以需要用 & 取出地址
fmt.Printf("%p\n", &arr)
}
5. スライスは参照型です
package main
import "fmt"
func main() {
// 数组:值类型——拷贝
arr1 := [3]int{
1, 2, 3} // 数组
arr2 := arr1
fmt.Println(arr1, arr2)
// 修改arr2不会影响arr1
arr2[2] = 888
fmt.Println(arr1, arr2)
fmt.Println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>")
// 切片:引用类型——地址
arr3 := []int{
1, 2, 3, 4, 5, 6}
arr4 := arr3
fmt.Println(arr3, arr4)
// 修改arr4会对arr4也进行修改
arr4[2] = 666
fmt.Println(arr3, arr4)
}
7.ディープコピー、シャローコピー
ディープコピー: コピーされるのはデータそのものです
- 値型データ、デフォルトは
- 深コピー:array、int、float、string、bool、sturct
- 浅いコピー: データのアドレスがコピーされ、複数の変数がメモリの同じブロックを指すようになります。
- 参照タイプのデータはデフォルトで浅いコピーです: スライス、マップ
- スライスは参照型のデータであるため、直接コピーしたものがアドレスになります。
package main
import "fmt"
func main() {
s1 := []int{
1, 2, 3, 4, 5}
s2 := make([]int, 0, 0)
// 通过 赋值的方式实现切片的深拷贝
for i := 0; i < len(s1); i++ {
s2 = append(s2, s1[i])
}
fmt.Println(s1)
fmt.Println(s2)
s2[1] = 88
fmt.Println(s1)
fmt.Println(s2)
// copy
s3 := []int{
4, 5, 6, 7, 8}
fmt.Println(s1)
fmt.Println(s3)
copy(s1, s3)
fmt.Println(s1)
fmt.Println(s3)
}
2. 地図
-
マップは、キーと値のペアの順序付けされていないコレクションです。Map の最も重要な点は、キーを通じてデータを迅速に取得することです。キーはインデックスに似ており、データの値を指します。
-
Map は一種のコレクションなので、配列やスライスと同様に反復処理できます。ただし、Map には順序がないため、返される順序を決定できません。
-
Map も参照型です。
1. マップの初期化
package main
import "fmt"
func main() {
/*
声明变量,默认 map 是 nil
var map_variable map[key_data_type]value_data_type
使用 make 函数
map_variable := make(map[key_data_type]value_data_type)
*/
// map 类型的定义: map[key]value nil
// 第一种方法:通过声明变量
var map1 map[int]string // 为 true,则为未创建
// 第二种:使用make
var map2 = make(map[string]string) // true nil 为创建 //map[] 创建
// 第三种:创建时直接赋值
var map3 = map[string]int{
"Go": 99, "Python": 88, "C": 66}
map2["hello1"] = "hello1"
map2["hello2"] = "hello2"
map2["hello3"] = "hello3"
map2["hello4"] = "hello4"
fmt.Println(map1)
fmt.Println(map2)
fmt.Println(map3)
fmt.Println(map1 == nil)
fmt.Println(map2 == nil)
}
2. 地図の利用
package main
import "fmt"
func main() {
// 创建 map
var map1 map[int]string //nil
map1 = make(map[int]string)
// 存储键值对,给键值对赋值
map1[1] = "guan01"
map1[2] = "guan02"
map1[3] = "guan03"
fmt.Println(map1)
// 获取 map 的数据
// 根据 key,来获取 value
// key 不存在,则获取默认的零值
fmt.Println(map1[1])
fmt.Println(map1[2])
fmt.Println(map1[3])
fmt.Println(map1[4])
// 可以通过 ok-idiom 来判断 key value 是否存在
value, ok := map1[1]
if ok == true {
fmt.Println("map key存在value", value)
} else {
fmt.Println("map key不存在")
}
value1, ok := map1[4]
if ok == true {
fmt.Println("map key存在value", value1)
} else {
fmt.Println("map key不存在")
}
// 修改数据
map1[1] = "luo01"
fmt.Println(map1)
// map 删除 delete
delete(map1, 2)
fmt.Println(map1)
// 如果 key 不存在就是新增,存在就是修改
map1[6] = "luo06"
fmt.Println(map1)
}
3. マップトラバース
地図を利用する際の注意点
- マップは順序付けされておらず、印刷されるマップは毎回異なります。インデックスからは取得できませんが、キーから取得する必要があります。
- マップの長さは固定ではありません。つまり、スライスと同様に参照型でもあります。
- 組み込みの len 関数はマップにも適用されます。今回はマップが持つキーの数です。
- マップのキーには、ブール、整数、浮動小数点、Ning 文字列など、同等のすべての型を使用できます。
package main
import "fmt"
func main() {
var map1 = map[string]int{
"Go": 99, "Python": 88, "Java": 66}
map2 := map1
// 多运行几次发现每次遍历的结果都不同,map是无序的
for k := range map1 {
fmt.Println(k, map1[k])
}
// 修改map2,发现map1 被修改了
map2["Go"] = 0
fmt.Println(map1)
}
4. スライスと組み合わせたマップ
必要:
- 1. 名前、年齢、性別、住所などの人々の情報を保存するマップを作成します。
- 2. 各マップには個人の情報が保存されます
- 3. これらのマップをスライスに保存します
- 4. トラバーサル出力を印刷する
package main
import "fmt"
func main() {
user1 := make(map[string]string)
user1["name"] = "guan001"
user1["age"] = "8"
user1["sex"] = "男"
user1["addr"] = "广西"
user2 := make(map[string]string)
user2["name"] = "guan002"
user2["age"] = "9"
user2["sex"] = "男"
user2["addr"] = "广东"
user3 := map[string]string{
"name": "guan03", "age": "10", "sex": "女", "addr": "四川"}
fmt.Println(user1)
fmt.Println(user2)
fmt.Println(user3)
userDatas := make([]map[string]string, 0, 3) //[]map[string]string为类型,0为长度,3为容量
userDatas = append(userDatas, user1)
userDatas = append(userDatas, user2)
userDatas = append(userDatas, user3)
fmt.Println(userDatas)
for _, v := range userDatas {
fmt.Printf("name:%s\n", v["name"])
fmt.Printf("age:%s\n", v["age"])
fmt.Printf("sex:%s\n", v["sex"])
fmt.Printf("addr:%s\n", v["addr"])
fmt.Println()
}
}