zoukankan      html  css  js  c++  java
  • GO语言基础_slice

    数据切片的设计和注意事项

    1. 切片和数组的区别:
      • 数组是需要指定个数的,而切片则不需要。数组赋值也可是使用如下方式,忽略元素个数,使用“...”代替
      • slice和array的关系十分密切,通过两者的合理构建,既能实现动态灵活的线性结构,也能提供访问元素的高效性能。当然,这种结构也不是完美无暇,共用底层数组,在部分修改操作的时候,可能带来副作用,同时如果一个很大的数组,那怕只有一个元素被切片应用,那么剩下的数组都不会被垃圾回收,这往往也会带来额外的问题。
      • 使用make创建slice,此时golang会生成一个匿名的数组。
      • append操作超过了原始切片的容量,将会有一个新建底层数组的过程,那么此时再修改函数返回切片,应该不会再影响原始切片。
    2. nil 是 interface、function、pointer、map、slice 和 channel 类型变量的默认初始值。
    3. 直接使用 slice:即使函数内部得到的是 slice 的值拷贝,但依旧会更新 slice 的原始数据(底层 array),会修改 slice 的底层 array,从而修改 slice
    4. 看起来 Go 支持多维的 array 和 slice,可以创建数组的数组、切片的切片,但其实并不是。对依赖动态计算多维数组值的应用来说,就性能和复杂度而言,用 Go 实现的效果并不理想。可以使用原始的一维数组、“独立“ 的切片、“共享底层数组”的切片来创建动态的多维数组。
      • 使用原始的一维数组:要做好索引检查、溢出检测、以及当数组满时再添加值时要重新做内存分配。
        • 使用“独立”的切片分两步:
          • 创建外部 slice
          • 对每个内部 slice 进行内存分配,注意内部的 slice 相互独立,使得任一内部 slice 增缩都不会影响到其他的 slice
        • 使用“共享底层数组”的切片
          • 创建一个存放原始数据的容器 slice
          • 创建其他的 slicefx
          • 切割原始 slice 来初始化其他的 slice
    5. string 类型的值是只读的二进制 byte slice,如果真要修改字符串中的字符,将 string 转为 []byte 修改后,再转为 string 即可
      • 因为一个 UTF8 编码的字符可能会占多个字节,比如汉字就需要 3~4 个字节来存储,此时更新其中的一个字节是错误的。
      • 更新字串的正确姿势:将 string 转为 rune slice(此时 1 个 rune 可能占多个 byte),直接更新 rune 中的字符
    6. 从 slice 中重新切出新 slice 时,新 slice 会引用原 slice 的底层数组。如果跳了这个坑,程序可能会分配大量的临时 slice 来指向原底层数组的部分数据,将导致难以预料的内存使用。可以通过拷贝临时 slice 的数据,而不是重新切片来解决(特别是对于返回slice的函数,在函数里面的新slice不要引用越来的slice,而是新建一个slice的拷贝,返回这个拷贝)

    测试代码

    package main
    
    import (
     "bytes"
     "fmt"
     "reflect"
    )
    
    func main() {
     makeSlice()
     apendSlice1()
     apendSlice2()
     fmt.Println("
    当往 newSlice 中新增元素的时候,由于其容量不够,newSlice 会拥有一个全新的底层数组,其容量是原来的两倍(Go 会自动完成这个操作,一旦元素个数超过 1000,增长因子会设为 1.25)")
     apendSlice3()
     apendSlice4()
     rangeSlice()
     fmt.Println("在使用 range 遍历 slice 的时候,range 会创建每个元素的副本,每次迭代的变量的地址是相同的,说明迭代过程复用了这个变量,也是一种防止内存浪费的做法。")
     equalSlice()
    }
    
    //makeSlice 数据切片的初始化
    func makeSlice() {
     //声明数据切片
     var slice1 []int //可以直接使用,直接使用append方法
    
     //初始化数据切片,指定数据切片的长度
     slice2 := make([]int, 10)
     //初始化数据切片,指定数据切片的长度和切片的最大容量
     slice3 := make([]int, 10, 100)
     //声明数据切片并赋值数据切片,此时数据切片的长度和最大容量相等
     slice4 := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
    
     fmt.Printf("slice1 = %v
     len(slice1) == %d,	 cap(slice1) == %d
    
    ", slice1, len(slice1), cap(slice1))
     fmt.Printf("slice2 = %v
     len(slice2) == %d,	 cap(slice2) == %d
    
    ", slice2, len(slice2), cap(slice2))
     fmt.Printf("slice3 = %v
     len(slice3) == %d,	 cap(slice3) == %d
    
    ", slice3, len(slice3), cap(slice3))
     fmt.Printf("slice4 = %v
     len(slice4) == %d,	 cap(slice4) == %d
    
    ", slice4, len(slice4), cap(slice4))
    }
    
    //apendSlice1 数据切片的追加
    func apendSlice1() {
     oldSlice := make([]int, 10, 100)
     fmt.Printf("oldSlice = %v
     len(oldSlice) == %d,	 cap(oldSlice) == %d
    
    ", oldSlice, len(oldSlice), cap(oldSlice))
    
     n, m := 1, 99 //0 <= n <= m < cap(oldSlice)
     newSlice := oldSlice[n:m] //[n:m)
     fmt.Printf("newSlice = %v
     len(newSlice) == %d,	 cap(newSlice) == %d
    
    ", newSlice, len(newSlice), cap(newSlice))
     fmt.Printf("len(newSlice) == m-n == %d, cap(newSlice) == cap(oldSlice)-n == %d
    ", len(newSlice), cap(newSlice))
    }
    
    //apendSlice2 数据切片的追加
    func apendSlice2() {
     //数据切片是一个引用,但是容量扩充之后引用就会发生变化
     fmt.Println("
    
    数据切片的复制
    ")
     parent_arr := [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
     fmt.Printf("parent_arr = %v
    ", parent_arr)
    
     child1_slice := parent_arr[0:10]
     fmt.Printf("child1_slice = %v len(child1_slice) == %d,	 cap(child1_slice) == %d
    
    ", child1_slice, len(child1_slice), cap(child1_slice))
    
     child2_slice := child1_slice[3:5]
     fmt.Printf("child2_slice = %v len(child2_slice) == %d,	 cap(child2_slice) == %d
    ", child2_slice, len(child2_slice), cap(child2_slice))
     child2_slice = append(child2_slice, 100)
     fmt.Printf("child2_slice = %v len(child2_slice) == %d,	 cap(child2_slice) == %d
    ", child2_slice, len(child2_slice), cap(child2_slice))
     fmt.Printf("child1_slice = %v len(child1_slice) == %d,	 cap(child1_slice) == %d
    
    ", child1_slice, len(child1_slice), cap(child1_slice))
    
     child3_slice := parent_arr[3:5]
     fmt.Printf("child3_slice = %v len(child3_slice) == %d,	 cap(child3_slice) == %d
    ", child3_slice, len(child3_slice), cap(child3_slice))
     child3_slice = append(child3_slice, 1000)
     fmt.Printf("child3_slice = %v len(child3_slice) == %d,	 cap(child3_slice) == %d
    ", child3_slice, len(child3_slice), cap(child3_slice))
     fmt.Printf("child1_slice = %v len(child1_slice) == %d,	 cap(child1_slice) == %d
    ", child1_slice, len(child1_slice), cap(child1_slice))
     fmt.Printf("child2_slice = %v len(child2_slice) == %d,	 cap(child2_slice) == %d
    ", child2_slice, len(child2_slice), cap(child2_slice))
     fmt.Println(&child3_slice[1], &child2_slice[1], "
    ")
    
     child4_slice := parent_arr[3:5:5]
     fmt.Printf("child4_slice = %v len(child4_slice) == %d,	 cap(child4_slice) == %d
    ", child4_slice, len(child4_slice), cap(child4_slice))
     child4_slice = append(child4_slice, 10000)
     fmt.Printf("child4_slice = %v len(child4_slice) == %d,	 cap(child4_slice) == %d
    ", child4_slice, len(child4_slice), cap(child4_slice))
     fmt.Printf("child1_slice = %v len(child1_slice) == %d,	 cap(child1_slice) == %d
    ", child1_slice, len(child1_slice), cap(child1_slice))
     fmt.Printf("child2_slice = %v len(child2_slice) == %d,	 cap(child2_slice) == %d
    ", child2_slice, len(child2_slice), cap(child2_slice))
     fmt.Printf("child3_slice = %v len(child3_slice) == %d,	 cap(child3_slice) == %d
    
    ", child3_slice, len(child3_slice), cap(child3_slice))
    
     child5_slice := parent_arr[3:5:6]
     fmt.Printf("child5_slice = %v len(child5_slice) == %d,	 cap(child5_slice) == %d
    ", child5_slice, len(child5_slice), cap(child5_slice))
     child5_slice = append(child5_slice, 100000)
     fmt.Printf("child5_slice = %v len(child5_slice) == %d,	 cap(child5_slice) == %d
    ", child5_slice, len(child5_slice), cap(child5_slice))
     fmt.Printf("child1_slice = %v len(child1_slice) == %d,	 cap(child1_slice) == %d
    ", child1_slice, len(child1_slice), cap(child1_slice))
     fmt.Printf("child2_slice = %v len(child2_slice) == %d,	 cap(child2_slice) == %d
    ", child2_slice, len(child2_slice), cap(child2_slice))
     fmt.Printf("child3_slice = %v len(child3_slice) == %d,	 cap(child3_slice) == %d
    ", child3_slice, len(child3_slice), cap(child3_slice))
     fmt.Printf("child4_slice = %v len(child4_slice) == %d,	 cap(child4_slice) == %d
    
    ", child4_slice, len(child4_slice), cap(child4_slice))
    
     child5_slice = append(child5_slice, 100000)
     fmt.Printf("child5_slice = %v len(child5_slice) == %d,	 cap(child5_slice) == %d
    ", child5_slice, len(child5_slice), cap(child5_slice))
     fmt.Printf("child1_slice = %v len(child1_slice) == %d,	 cap(child1_slice) == %d
    ", child1_slice, len(child1_slice), cap(child1_slice))
     fmt.Printf("child2_slice = %v len(child2_slice) == %d,	 cap(child2_slice) == %d
    ", child2_slice, len(child2_slice), cap(child2_slice))
     fmt.Printf("child3_slice = %v len(child3_slice) == %d,	 cap(child3_slice) == %d
    ", child3_slice, len(child3_slice), cap(child3_slice))
     fmt.Printf("child4_slice = %v len(child4_slice) == %d,	 cap(child4_slice) == %d
    ", child4_slice, len(child4_slice), cap(child4_slice))
    }
    
    //apendSlice3 数据切片的追加
    func apendSlice3() {
     //数据切片声明的时候,直接可以通过append添加元素
     //验证数据切片的容量是按照倍数增长的
     var slice []int
     i := 0
     for i < 10 {
      fmt.Printf("slice = %v
     len(slice) == %d,	 cap(slice) == %d
    
    ", slice, len(slice), cap(slice))
      slice = append(slice, i)
      i++
     }
     fmt.Printf("slice = %v
     len(slice) == %d,	 cap(slice) == %d
    
    ", slice, len(slice), cap(slice))
    
     //直接使用 slice:即使函数内部得到的是 slice 的值拷贝,但依旧会更新 slice 的原始数据(底层 array)
     //会修改 slice 的底层 array,从而修改 slice
     func(arr []int) {
      arr[0] = 7
      fmt.Println(slice)
     }(slice)
    
     fmt.Println(slice)
    }
    
    //apendSlice4 数据切片的追加
    func apendSlice4() {
     slice := [][]int{{10}, {100, 200}}
    
     fmt.Printf("slice = %v
     len(slice) == %d,	 cap(slice) == %d
    
    ", slice, len(slice), cap(slice))
     fmt.Printf("slice[0] = %v
     len(slice[0]) == %d,	 cap(slice[0]) == %d
    
    ", slice[0], len(slice[0]), cap(slice[0]))
     fmt.Printf("slice[1] = %v
     len(slice[1]) == %d,	 cap(slice[1]) == %d
    
    ", slice[1], len(slice[1]), cap(slice[1]))
    
     slice[0] = append(slice[0], 20)
     fmt.Printf("slice = %v
     len(slice) == %d,	 cap(slice) == %d
    
    ", slice, len(slice), cap(slice))
     fmt.Printf("slice[0] = %v
     len(slice[0]) == %d,	 cap(slice[0]) == %d
    
    ", slice[0], len(slice[0]), cap(slice[0]))
     fmt.Printf("slice[1] = %v
     len(slice[1]) == %d,	 cap(slice[1]) == %d
    
    ", slice[1], len(slice[1]), cap(slice[1]))
    }
    
    //rangeSlice 数据切片的遍历
    func rangeSlice() {
     slice := []int{10, 20, 30, 40,} //这个","不是必须的,但是如果换行之后这个","就是必须的
    
     // 迭代每个元素,并显示值和地址,这个值是原来元素值的一份拷贝,修改这个值并不会改变原来元素的值
     for index, value := range slice {
      fmt.Printf("Value: %d Value-Addr: %X ElemAddr: %X
    ", value, &value, &slice[index])
      value *=10
     }
     fmt.Println(slice)
     //需要使用数据切片的引用才能实现对源数据的修改
     for index, value := range slice {
      fmt.Printf("Value: %d Value-Addr: %X ElemAddr: %X
    ", value, &value, &slice[index])
      slice[index] *=10
     }
     fmt.Println(slice)
    }
    
    //equalSlice 数据切片的比较
    func equalSlice(){
     var slice1 []byte
     slice2 := []byte{}
     fmt.Println("slice1 == slice1: ", bytes.Equal(slice1, slice2))
     fmt.Println("slice1 == slice2: ", reflect.DeepEqual(slice1, slice2))
    }
    

    测试代码结果

    slice1 = []
     len(slice1) == 0,	cap(slice1) == 0
    
    slice2 = [0 0 0 0 0 0 0 0 0 0]
     len(slice2) == 10,	cap(slice2) == 10
    
    slice3 = [0 0 0 0 0 0 0 0 0 0]
     len(slice3) == 10,	cap(slice3) == 100
    
    slice4 = [1 2 3 4 5 6 7 8 9 10]
     len(slice4) == 10,	cap(slice4) == 10
    
    oldSlice = [0 0 0 0 0 0 0 0 0 0]
     len(oldSlice) == 10,	cap(oldSlice) == 100
    
    newSlice = [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
     len(newSlice) == 98,	cap(newSlice) == 99
    
    len(newSlice) == m-n == 98, cap(newSlice) == cap(oldSlice)-n == 99
    
    
    数据切片的复制
    
    parent_arr = [1 2 3 4 5 6 7 8 9 10]
    child1_slice = [1 2 3 4 5 6 7 8 9 10] len(child1_slice) == 10,	cap(child1_slice) == 10
    
    child2_slice = [4 5] len(child2_slice) == 2,	cap(child2_slice) == 7
    child2_slice = [4 5 100] len(child2_slice) == 3,	cap(child2_slice) == 7
    child1_slice = [1 2 3 4 5 100 7 8 9 10] len(child1_slice) == 10,	cap(child1_slice) == 10
    
    child3_slice = [4 5] len(child3_slice) == 2,	cap(child3_slice) == 7
    child3_slice = [4 5 1000] len(child3_slice) == 3,	cap(child3_slice) == 7
    child1_slice = [1 2 3 4 5 1000 7 8 9 10] len(child1_slice) == 10,	cap(child1_slice) == 10
    child2_slice = [4 5 1000] len(child2_slice) == 3,	cap(child2_slice) == 7
    0xc0000880c0 0xc0000880c0 
    
    child4_slice = [4 5] len(child4_slice) == 2,	cap(child4_slice) == 2
    child4_slice = [4 5 10000] len(child4_slice) == 3,	cap(child4_slice) == 4
    child1_slice = [1 2 3 4 5 1000 7 8 9 10] len(child1_slice) == 10,	cap(child1_slice) == 10
    child2_slice = [4 5 1000] len(child2_slice) == 3,	cap(child2_slice) == 7
    child3_slice = [4 5 1000] len(child3_slice) == 3,	cap(child3_slice) == 7
    
    child5_slice = [4 5] len(child5_slice) == 2,	cap(child5_slice) == 3
    child5_slice = [4 5 100000] len(child5_slice) == 3,	cap(child5_slice) == 3
    child1_slice = [1 2 3 4 5 100000 7 8 9 10] len(child1_slice) == 10,	cap(child1_slice) == 10
    child2_slice = [4 5 100000] len(child2_slice) == 3,	cap(child2_slice) == 7
    child3_slice = [4 5 100000] len(child3_slice) == 3,	cap(child3_slice) == 7
    child4_slice = [4 5 10000] len(child4_slice) == 3,	cap(child4_slice) == 4
    
    child5_slice = [4 5 100000 100000] len(child5_slice) == 4,	cap(child5_slice) == 6
    child1_slice = [1 2 3 4 5 100000 7 8 9 10] len(child1_slice) == 10,	cap(child1_slice) == 10
    child2_slice = [4 5 100000] len(child2_slice) == 3,	cap(child2_slice) == 7
    child3_slice = [4 5 100000] len(child3_slice) == 3,	cap(child3_slice) == 7
    child4_slice = [4 5 10000] len(child4_slice) == 3,	cap(child4_slice) == 4
    
    当往 newSlice 中新增元素的时候,由于其容量不够,newSlice 会拥有一个全新的底层数组,其容量是原来的两倍(Go 会自动完成这个操作,一旦元素个数超过 1000,增长因子会设为 1.25)
    slice = []
     len(slice) == 0,	cap(slice) == 0
    
    slice = [0]
     len(slice) == 1,	cap(slice) == 1
    
    slice = [0 1]
     len(slice) == 2,	cap(slice) == 2
    
    slice = [0 1 2]
     len(slice) == 3,	cap(slice) == 4
    
    slice = [0 1 2 3]
     len(slice) == 4,	cap(slice) == 4
    
    slice = [0 1 2 3 4]
     len(slice) == 5,	cap(slice) == 8
    
    slice = [0 1 2 3 4 5]
     len(slice) == 6,	cap(slice) == 8
    
    slice = [0 1 2 3 4 5 6]
     len(slice) == 7,	cap(slice) == 8
    
    slice = [0 1 2 3 4 5 6 7]
     len(slice) == 8,	cap(slice) == 8
    
    slice = [0 1 2 3 4 5 6 7 8]
     len(slice) == 9,	cap(slice) == 16
    
    slice = [0 1 2 3 4 5 6 7 8 9]
     len(slice) == 10,	cap(slice) == 16
    
    [7 1 2 3 4 5 6 7 8 9]
    [7 1 2 3 4 5 6 7 8 9]
    slice = [[10] [100 200]]
     len(slice) == 2,	cap(slice) == 2
    
    slice[0] = [10]
     len(slice[0]) == 1,	cap(slice[0]) == 1
    
    slice[1] = [100 200]
     len(slice[1]) == 2,	cap(slice[1]) == 2
    
    slice = [[10 20] [100 200]]
     len(slice) == 2,	cap(slice) == 2
    
    slice[0] = [10 20]
     len(slice[0]) == 2,	cap(slice[0]) == 2
    
    slice[1] = [100 200]
     len(slice[1]) == 2,	cap(slice[1]) == 2
    
    Value: 10 Value-Addr: C000074DC8 ElemAddr: C000090060
    Value: 20 Value-Addr: C000074DC8 ElemAddr: C000090068
    Value: 30 Value-Addr: C000074DC8 ElemAddr: C000090070
    Value: 40 Value-Addr: C000074DC8 ElemAddr: C000090078
    [10 20 30 40]
    Value: 10 Value-Addr: C000074E10 ElemAddr: C000090060
    Value: 20 Value-Addr: C000074E10 ElemAddr: C000090068
    Value: 30 Value-Addr: C000074E10 ElemAddr: C000090070
    Value: 40 Value-Addr: C000074E10 ElemAddr: C000090078
    [100 200 300 400]
    在使用 range 遍历 slice 的时候,range 会创建每个元素的副本,每次迭代的变量的地址是相同的,说明迭代过程复用了这个变量,也是一种防止内存浪费的做法。
    slice1 == slice1: true
    slice1 == slice2: false
    
  • 相关阅读:
    ros::NodeHandle成员介绍
    ros::NodeHandle成员介绍
    odom_out_and_back.cpp
    odom_out_and_back.cpp
    c++实现ros by example volume1 例子timed_out_and_back功能
    SQL语句进阶
    数据库基础(常用的SQL)
    安装Mysql
    1.初识数据库
    (MYSQL错误解决)本地计算机上的MYSQL服务启动停止后,某些服务在未由其他服务或程序使用时将自动停止
  • 原文地址:https://www.cnblogs.com/hudiblogs/p/10119493.html
Copyright © 2011-2022 走看看