【GOプログラミング言語】スライスとマップ

スライスとマップ



ここに画像の説明を挿入


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()
	}
}

ここに画像の説明を挿入

おすすめ

転載: blog.csdn.net/guanguan12319/article/details/130667593