切片和map Golang

切片

  • 数组

    • 一组数

    • 数组是值传递的

    • 创建数组 [size]int

    • 数组的大小是不可变的

    • 二维数组,数组套数组

    • 冒泡排序

  • 切片

    • 切片本身是不存在数据的

    • 底层是指向了一个数组

    • 如果我们存放的数据大于了切片的容量,在底层就会扩容 copy > 1024 1.25倍

使用数组来创建切片

package main
​
import "fmt"
​
// 切片本身是不保存数据的,只是指向了底层数组。
func main() {
   // 数组 [0,10)   数组的截取 [start,end]
   // 数组
   arr := [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} // 0xc000018230
   // 通过数组来创建切片 [start,end)
   /*
     arr = 10个数
     1,  0xc000000001
     2,  0xc000000002
     3,  0xc000000003
     4,  0xc000000004
     5,  0xc000000005
   */
   s1 := arr[:5]  // 1-5   // 0xc000018230 {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
   s2 := arr[3:8] // 4-8   // 0xc000018248
   s3 := arr[5:]  //  6-10 // 0xc000018258
   s4 := arr[:]   //  0-10 // 0xc000018230
​
   fmt.Println(s1)
   fmt.Println(s2)
   fmt.Println(s3)
   fmt.Println(s4)
   // 查看容量和长度
   fmt.Printf("s1 len:%d, cap:%d\n", len(s1), cap(s1)) // s1 len:5, cap:10
   fmt.Printf("s2 len:%d, cap:%d\n", len(s2), cap(s2)) // s2 len:5, cap:7
   fmt.Printf("s3 len:%d, cap:%d\n", len(s3), cap(s3)) // s3 len:5, cap:5
   fmt.Printf("s4 len:%d, cap:%d\n", len(s4), cap(s4)) // s4 len:10, cap:10
​
   // 内存地址
   fmt.Printf("%p\n", s1) // 指向了原数组
   fmt.Printf("%p\n", s2) // 截断了后,指向了最新截断的元素的下标开始
   fmt.Printf("%p\n", s3)
   fmt.Printf("%p\n", &arr[5])
   fmt.Printf("%p\n", s4) // 指向了原数组
   fmt.Printf("%p\n", &arr)
​
   // 修改数组的内容, 切片也随之发生了变化 (切:切片不保存数据-->底层的数组 )
   arr[2] = 100
   fmt.Println(arr) // [1 2 100 4 5 6 7 8 9 10]
   fmt.Println(s1)  // [1 2 100 4 5]
   fmt.Println(s2)  // [4 5 6 7 8]
   fmt.Println(s3)  // [6 7 8 9 10]
   fmt.Println(s4)  // [1 2 100 4 5 6 7 8 9 10]
   fmt.Println("------------------------")
   // 修改切片的内容,发现数组也随之发生了变化。(本质:修改的都是底层的数组)
   s2[2] = 80
   fmt.Println(arr) // [1 2 100 4 5 80 7 8 9 10]
   fmt.Println(s1)  // [1 2 100 4 5]
   fmt.Println(s2)  // [4 5 80 7 8]
   fmt.Println(s3)  // [80 7 8 9 10]
   fmt.Println(s4)  // [1 2 100 4 5 80 7 8 9 10]
   fmt.Println("------------------------")
   // 切片扩容
   s1 = append(s1, 11, 12, 13, 14, 15, 16)
   fmt.Println(arr) // [1 2 100 4 5 80 7 8 9 10]
   // 如果在切片容量内加,会导致原来的数组数据发生修改。只有扩容之后,才会导致底层数组会产生拷贝。新的数组
   fmt.Println(s1) // [1 2 100 4 5 11 12 13 14 15 16]
   fmt.Println(s2) // [4 5 80 7 8]
   fmt.Println(s3) // [80 7 8 9 10]
   // 查看内存地址,已经不是同一个了
   fmt.Printf("%p\n", s1)   // 0xc0000d2000
   fmt.Printf("%p\n", &arr) // 0xc0000ac0f0
​
}

切片:引用类型

package main
​
import "fmt"
​
func main() {
   // 值类型,传递:copy
   arr1 := [4]int{1, 2, 3, 4}
   arr2 := arr1
   fmt.Println(arr1, arr2)
   arr1[0] = 100
   fmt.Println(arr1, arr2)
​
   // 切片:引用类型
   s1 := []int{1, 2, 3, 4}
   s2 := s1
   fmt.Println(s1, s2)
   s1[0] = 100
   fmt.Println(s1, s2)
   // 通过内存分析发现,s1,s2指向了同一个地址。
   fmt.Printf("%p\n", s1)
   fmt.Printf("%p\n", s2)
}

深拷贝、浅拷贝

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

  • 值类型的数据,默认都是深拷贝,array、int、float、string、bool、struct....

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

  • 引用类型的数据: slice、map

  • 因为切片是引用类的数据,直接拷贝的是这个地址

切片怎么实现深拷贝 copy

package main
​
import "fmt"
​
// 切片实现深拷贝
func main() {
   // 将原来切片中的数据拷贝到新切片中
   s1 := []int{1, 2, 3, 4}
   s2 := make([]int, 0) // len:0 cap:0
   for i := 0; i < len(s1); i++ {
      s2 = append(s2, s1[i])
   }
   fmt.Println(s1)
   fmt.Println(s2)
   s1[0] = 100
   fmt.Println(s1)
   fmt.Println(s2)
​
   // copy
   s3 := []int{5, 6, 7}
   fmt.Println(s2)
   fmt.Println(s3)
​
   // 将s3中的元素拷贝到s2中
   //copy(s2, s3)
   // 将s2中的元素拷贝到s3中
   copy(s3, s2)
   fmt.Println(s2)
   fmt.Println(s3)
}

函数中参数传递问题

按照数据的存储特点来分:

  • 值类型的数据:操作的是数据本身、int 、string、bool、float64、array...

  • 引用类型的数据:操作的是数据的地址 slice、map、chan....

值传递

引用传递

package main
​
import "fmt"
​
func main() {
   arr1 := [4]int{1, 2, 3, 4}
   fmt.Println("arr1:", arr1)
   update(arr1)
   fmt.Println("end arr1:", arr1)
​
   s1 := []int{1, 2, 3, 4}
   fmt.Println("s1:", s1)
   update2(s1)
   fmt.Println("end s1:", s1)
}
​
// 函数补充:在使用函数的时候,一定要特别注意参数问题,如果是值类型的,很多传递是无效的。
// 一些值传递的类型的参数,如果我们想通过函数来进行修改对应的值,这个时候就需要使用指针
​
// 指针变量 -> 指向原来变量的地址
​
// 数组是值类型的
func update(arr [4]int) {
   fmt.Println("--> arr:", arr)
   arr[0] = 100
   fmt.Println("--> end arr:", arr)
}
​
// 切片是引用类型的
func update2(s []int) {
   fmt.Println("--> s:", s)
   s[0] = 100
   fmt.Println("--> end s:", s)
}

map

Map 是一种无序的键值对的集合。

  • 无序 :map[key]

  • 键值对:key - value /key - value key - value key - value

Map 最重要的一点是通过 key 来快速检索数据,key 类似于索引,指向数据的值。

Map 是一种集合,所以我们可以像迭代数组和切片那样迭代它。

不过,Map 是无序的,我们无法决定它的返回顺序。

Map也是引用类型

map的定义

package main
​
import "fmt"
​
// map 集合,保存数据的一种结构
func main() {
   // 创建一个map,也是一个变量,数据类型是 map
   // map[key]value
   var map1 map[int]string // 只是声明了但是没有初始化,不能使用 nil
   if map1 == nil {
      fmt.Println("map1==nil")
   }
   // 更多的时候使用的是make方法创建
   var map2 = make(map[string]string) // 创建了map
   fmt.Println(map1)
   fmt.Println(map2)
   // 在创建的时候,添加一些基础数据
   // map[string]int nil
   // map[string]int {key:value,key:value,......}
   var map3 = map[string]int{"Go": 100, "Java": 10, "C": 60}
   fmt.Println(map3) 
   // 关于map的类型,就如定义的一般 map[string]int
   // 类型主要是传参要确定
   fmt.Printf("%T\n", map3)
}

map的使用

  • 创建并初始化map

  • map[key] = value 。将vaule赋值给对应的map的key

  • 判断key是否存在,value, ok = map[key]

  • 删除map中的元素, delete(map,key)

  • 新增 map[key] = value

  • 修改 map[key] = value ,如果存在这个key就是修改。

  • 查看map的大小, len(map)

package main
​
import "fmt"
​
// 内存中的一个简单的数据库。key - value 。 增删改查的。
​
// map的使用
// 定义  var m1 map[key]value    make(map[key]value)
func main() {
  // 创建map
  var map1 map[int]string // nil,不能使用的,只是声明了,还没有初始化
  // 给map1赋值
  // 初始化map
  map1 = make(map[int]string)
  // 在map中如果你的key重复了,它就会覆盖这个key原来的值,一个key只能对应一个value
  map1[100] = "学相伴"
  map1[100] = "学相伴1111"
  map1[200] = "狂神说"
​
  // 获取数据  map[key]
  fmt.Println(map1)
  fmt.Println(map1[200])
  fmt.Println(map1[1]) // 不存在,默认值 string ""
​
  // map中,没有index下标,有时候我们取值就需要判断这个key是否存在了
  // map中的判断,ok-idiom 是否存在的
  // value = map[key] , 隐藏的返回值 ok-idiom , 可选参数
  value, ok := map1[1]
  //
  if ok {
    fmt.Println("map key 存在的,value:", value)
  } else {
    fmt.Println("map key 不存在的")
  }
​
  // 修改数据
  map1[100] = "飞哥"
  fmt.Println(map1)
  // 如果数据存在,修改它,如果不存在,就会创建对应的 key-value
  map1[1] = "xxxxxxxxxxxxxxx"
  fmt.Println(map1)
​
  // map中的数据删除问题  delete
  delete(map1, 1)
  fmt.Println(map1)
​
  // map的大小
  fmt.Println(len(map1))
​
}

map遍历

for k, v := range map1 , key-value,每次打印可能是无序的。

package main
​
import "fmt"
​
/*
     遍历map
      - key、value 无序的,遍历map,可能每次的结果排序都不一致。
       - "aaa" "aaaaa"
       - "bbb" "bbbbb"
       - "ccc" "ccccc"
​
1、map是无序的,每次打印出来的map可能都不一样,它不能通过index获取,只能通过key来获取
2、map的长度是不固定的,是引用类型的
3、len可以用于map查看map中数据的数量,但是cap无法使用
4、map的key 可以是 布尔类型,整数,浮点数,字符串
*/
func main() {
   var map1 = map[string]int{"Go": 100, "Java": 99, "C": 80, "Python": 60}
   // 循环遍历,只能通过for range
   // 返回 key 和 value
   for k, v := range map1 {
      fmt.Println(k, v)
   }
}

map结合slice使用

package main
​
import "fmt"
​
// map 结合 slice 来使用
​
/*
需求:
1、创建map来存储人的信息,name,age,sex,addr
2、每个map保存一个的信息
3、将这些map存入到切片中
4、打印这些数据
*/
func main() {
   
   user1 := make(map[string]string)
   user1["name"] = "kuangshen"
   user1["age"] = "27"
   user1["sex"] = "男"
   user1["addr"] = "重庆"
​
   user2 := make(map[string]string)
   user2["name"] = "feige"
   user2["age"] = "30"
   user2["sex"] = "男"
   user2["addr"] = "长沙"
​
   user3 := map[string]string{"name": "小蓝", "age": "18", "sex": "男", "addr": "火星"}
   fmt.Println(user3)
​
   // 3个数据有了,存放到切片中,供我们使用
   userDatas := make([]map[string]string, 0, 3)
   userDatas = append(userDatas, user1)
   userDatas = append(userDatas, user2)
   userDatas = append(userDatas, user3)
​
   fmt.Println(userDatas)
​
   // 0 map[string]string
   for _, user := range userDatas {
      //fmt.Println(i)
      fmt.Println(user["addr"])
   }
​
}

作业

1、通过map和slice 写一个简单的图书数据库,并可以通过书名或者书号来查询书的位置

books
 -book : num,name,addr,xxxx ,xx

2、了解函数传参中,引用类型和值类型的问题

  • 值类型的数据,无法修改原来的数据的。 如果要修改-->1、指针 2/直接操作地址。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值