文章

切片与map

切片Slice

Go语言切片是对数组的抽象

切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大

切片与数组相比,不需要设定长度,在[]中不用设定值,相对来说比较自由

从概念来说,slice像一个结构体,这个结构体包含了三个元素:

指针:指向数组中slice指定的开始位置

长度:即slice的长度

最大长度:也就是slice开始位置到数组的最后位置的长度

定义切片

package main

import "fmt"

func main() {
   //数组的长度一旦定义就是不可以改变的
   arr := [4]int{1, 2, 3, 4}
   fmt.Println(arr)
   //定义切片:长度就是可变的 []int
   var s1 []int
   fmt.Println(s1)
   if s1 == nil {
      fmt.Println("切片为空")
   }
   s2 := []int{1, 2, 3, 4, 5, 6, 7}
   fmt.Println(s2)
   fmt.Printf("%T,%T\n", s1, arr) //[]int,[4]int
   fmt.Println(s2[0])
   fmt.Println(s2[1])
   for i := range s2 {
      fmt.Print(s2[i], " ")
   }

}

image-20221219132319976

make函数创建切片

package main

import "fmt"

func main() {
   s1 := make([]int, 5, 10)
   fmt.Println(s1)
   fmt.Println("长度为:", len(s1))
   fmt.Println("容量为:", cap(s1))
   s1[1] = 100
   //s1[6] = 200//panic: runtime error: index out of range [6] with length 5
   fmt.Println(s1)
}

image-20221219151203668

切片扩容和遍历

package main

import "fmt"

func main() {
   //append扩容
   s1 := make([]int, 0, 5)
   s1 = append(s1, 1, 2, 3)
   fmt.Println(s1)
   //如果切片中的数据超过了规定的容量,那么他就会自动扩容
   s1 = append(s1, 4, 5, 6, 7, 8, 9)
   fmt.Println(s1)
   s2 := []int{1, 2, 3, 1, 23, 4}
   s1 = append(s1, s2...)
   fmt.Println(s1)

   for i := 0; i < len(s1); i++ {
      fmt.Print(s1[i], " ")
   }
   fmt.Println()
   for _, v := range s1 {
      fmt.Print(v, " ")
   }
}

image-20221219151934888

扩容的内存分析

package main

import "fmt"

func main() {
   s1 := []int{1, 2, 3}
   fmt.Println(s1)
   fmt.Printf("len:%d,cap:%d\n", len(s1), cap(s1)) //len:3 cap:3

   s1 = append(s1, 4, 5)
   fmt.Println(s1)
   fmt.Printf("len:%d,cap:%d\n", len(s1), cap(s1)) //len:5,cap:6
   fmt.Printf("%p\n", s1)

   s1 = append(s1, 6, 7, 8)
   fmt.Println(s1)
   fmt.Printf("len:%d,cap:%d\n", len(s1), cap(s1)) //len:8,cap:12
   fmt.Printf("%p\n", s1)

   s1 = append(s1, 9, 10)
   fmt.Println(s1)
   fmt.Printf("len:%d,cap:%d\n", len(s1), cap(s1)) //len:10,cap:12
   fmt.Printf("%p\n", s1)

   s1 = append(s1, 11, 12, 13)
   fmt.Println(s1)
   fmt.Printf("len:%d,cap:%d\n", len(s1), cap(s1)) //len:13,cap:24
   fmt.Printf("%p\n", s1)
}

image-20221219155551650

扩容的内存分析

  • 每一个切片引用了一个底层数组
  • 切片本身不存储任何数据,都是这个底层数组存储,所以修改切片也就是修改这个数组中的数据
  • 当向切片中添加数据时,如果没有超过容量,直接添加,如果超过容量,自动扩容,成倍增加
package main

import "fmt"

func main() {
   numbers := []int{1, 2, 3}
   fmt.Printf("len=%d,cap=%d,slice=%v", len(numbers), cap(numbers), numbers)

   //创建切片,numbers1 是之前切片的2倍容量
   numbers1 := make([]int, len(numbers), (cap(numbers))*2)
   fmt.Println()
   //拷贝numbers的内容到number1
   copy(numbers1, numbers)
   fmt.Printf("len=%d,cap=%d,slice=%v", len(numbers1), cap(numbers1), numbers1)

}

image-20221219163215325

  • 切片一旦扩容,就是重新指向一个新的底层数组

在已有数组上创建切片

从已有数组上,直接创建切片,该切片的底层数组就是当前的数组,长度是从start到end切割到数据量,但是容器从start到数组的末尾

slice := arr[start:end]
package main

import "fmt"

func main() {
   arr := [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
   //           0, 1,2,3, 4,5, 6,7,8, 9
   fmt.Println("=============通过数组创建切片=============")
   //arr[start包含:end不包含]
   s1 := arr[:5]  //1-5
   s2 := arr[3:8] //4-8
   s3 := arr[5:]  //6-10
   s4 := arr[:]   //1-10
   fmt.Println(s1)
   fmt.Println(s2)
   fmt.Println(s3)
   fmt.Println(s4)
   fmt.Printf("%p\n", &arr)
   fmt.Printf("%p\n", s1)
   fmt.Printf("len:%d,cap:%d\n", len(s1), cap(s1))
   fmt.Printf("len:%d,cap:%d\n", len(s2), cap(s2))
   fmt.Printf("len:%d,cap:%d\n", len(s3), cap(s3))
   fmt.Printf("len:%d,cap:%d\n", len(s4), cap(s4))

   arr[0] = 100
   fmt.Println(arr)
   fmt.Println(s1)
   arr[1] = 200
   fmt.Println(arr)
   fmt.Println(s1)
   s1 = append(s1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1)
   fmt.Println(arr)
   fmt.Println(s1)
   s1[0] = 400
   fmt.Println(arr)
   fmt.Println(s1)
   fmt.Printf("%p\n", &arr)
   fmt.Printf("%p\n", s1)
}

image-20221221102531157

切片:引用类型

package main

import "fmt"

func main() {
   //数组,值类型
   arr1 := [4]int{1, 2, 3, 4}
   arr2 := arr1
   fmt.Println(arr1, arr2)
   arr2[0] = 100
   fmt.Println(arr1, arr2)
   fmt.Printf("arr1:%p\narr2:%p\n", &arr1, &arr2)
   fmt.Println("========================")
   //切片,引用类型
   s1 := []int{1, 2, 3, 4}
   s2 := s1
   fmt.Println(s1, s2)
   s2[0] = 100
   fmt.Println(s1, s2)
   fmt.Printf("s1:%p\ns2:%p", s1, s2)

}

image-20221221104449011

深拷贝、浅拷贝

深拷贝:拷贝的是数据本身

  • 值类型的数据。默认都是深拷贝:array,int,float,string,bool,sturct

浅拷贝:拷贝的是数据的地址,会导致多个变量指向同一块内存

  • 引用类型的数据,默认都是浅拷贝:slice,map
  • 因为切片是引用类型的数据,直接拷贝的是地址

实现切片深拷贝

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[0] = 100
   fmt.Println(s1)
   fmt.Println(s2)
   fmt.Println("=============")
   //copy函数实现深拷贝
   s3 := []int{4, 5, 6}
   fmt.Println(s3)
   fmt.Println(s1)
   copy(s1, s3)
   fmt.Println(s3)
   fmt.Println(s1)
}

image-20221221111650893

集合Map

Map是一种无序的键值对的集合。Map最重要的一点是通过key来快速检索数据,key类似于索引,指向数据的值。

Map是一种集合,所以我们可以像迭代数组和切片那样迭代它。不过,Map是无序的,我们无法决定它的返回顺序。

Map也是引用类型

map[key]value
package main

import "fmt"

func main() {

   //map 类型的定义:map[key]value
   var map1 map[int]string            // true nil 未创建
   var map2 = make(map[string]string) // map[] 创建
   var map3 = map[string]int{"Go": 100, "Java": 90, "C": 60}
   fmt.Println(map1) //nil
   fmt.Println(map2)
   fmt.Println(map3)

   fmt.Println(map1 == nil)
   fmt.Println(map2 == nil)
   for k, v := range map3 {
      fmt.Println(k, v)
   }
   map2["yama"] = "zuo"
   fmt.Println(map2)
}

image-20221221114449430

map的使用

package main

import "fmt"

func main() {
   //创建map
   map1 := make(map[int]string)
   //存储键值对,给键值对赋值
   map1[1] = "yama"
   map1[2] = "go"
   map1[3] = "zhao"
   map1[4] = "zuo"
   fmt.Println(map1)
   fmt.Println(map1[1]) //根据key,来获取value
   fmt.Println(map1[5]) //key不存在,则获取默认的零值""

   //可以通过ok-idiom来判断key value是否存在
   value, ok := map1[3]
   if ok {
      fmt.Println("map key存在的,value,", value)
   } else {
      fmt.Println("map key不存在")
   }

   map1[1] = "我修改了数据"
   fmt.Println(map1[1])
  //map 删除 delete(map,key)
   delete(map1, 1)
   fmt.Println(map1)
   fmt.Println(len(map1))
   map1[10] = "aaa"
   fmt.Println(len(map1))

}

image-20221221144348654

map的遍历

⚠️⚠️

  • map是无序的,每次打印出来的map都会不一样,它不能通过index获取,而必须通过key获取
  • map的长度是不固定的,也就是和slice一样,也是一种引用类型
  • 内置的len函数同样适用于map,返回map拥有的key的数量
  • map的key可以是所有可比较的类型,如布尔型、整数型、浮点型、字符串…
package main

import "fmt"

func main() {
	var map1 = map[string]int{"go": 100, "python": 99, "c": 98}
	//每次打印出来的结果都不一样
	for k, v := range map1 {
		fmt.Println(k, v)
	}
	map2 := map1
	map2["go"] = 0
	fmt.Println(map1)
}

image-20221221151534812

map结合slice

需求

  1. 创建map来存储人的信息:name,age,sex,addr
  2. 每个map存一个人的信息
  3. 将这些map存入到slice中
  4. 打印遍历输出
package main

import "fmt"

func main() {
	user1 := make(map[string]string)
	user1["name"] = "zuo"
	user1["age"] = "18"
	user1["sex"] = "男"
	user1["addr"] = "深圳"
	user2 := make(map[string]string)
	user2["name"] = "zhao"
	user2["age"] = "19"
	user2["sex"] = "男"
	user2["addr"] = "深圳"
	user3 := map[string]string{"name": "jiang", "age": "20", "sex": "女", "addr": "重庆"}
	fmt.Println(user1)
	fmt.Println(user2)
	fmt.Println(user3)

	userDatas := make([]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 ", v["name"])
		fmt.Printf("age:%s ", v["age"])
		fmt.Printf("sex:%s ", v["sex"])
		fmt.Printf("addr:%s\n", v["addr"])
	}
}

image-20221221160131225

License:  CC BY 4.0 test