• go 切片和数组


    一、区别

      数组的长度是固定的,初始化后就不能修改长度,大家平时代码中比较少用。

      slice是对数组的一个封装,可以动态扩容,slice是一个结构体,包含三个字段:底层数组、长度、容量

     二、初始化方式

      数组

    var a = [4]int{1,2,3,4}
    var b = [...]int{1,2,3,4,5}

      切片

    var a []int //当前值为nil,未分配内存空间
    var b = []int{1, 2, 3}
    var c = make([]int, 5) //初始化5个元素,默认值为0,容量为5,在后面追加元素就会触发扩容
    var d = make([]int, 0, 5) //初始化0个元素,容量为5

    注意:slice在追加元素时如果容量不够会触发扩容,原slice拷贝到新slice中,扩容规则网上的说法:原slice小于1024,新的slice容量变成原slice的2倍,大于1024时,新slice容量变成原slice的1.25倍。这里的说法只是一个大概不是很准确,感兴趣的朋友可以查看网络上相关文章或源码,其实这对于我们平时开发影响不是很大,想要最求性能可以在初始化时预估容量,减少扩容和拷贝的次数。

    三、拷贝

      第一种

    var a = []int{1,2,3,4,5}
    var b = a[2:4]  //拷贝a中部分数据到b中
    
    b[0] = 12 //改变b第一个元素的值
    log.Println(a,b) //输出:[1 2 12 4 5] [12 4] 两个数组的值都被改变了,说明数据是地址拷贝
    
    b = append(b,6,7,8,9) //追加元素,容量不够引发扩容
    b[0] = 13 //改变b第一个元素的值
    log.Println(a,b) //输出:[1 2 12 4 5] [13 4 6 7 8 9] b中的数据改变了,a中的值并没有改变,扩容以后b和a中的元素已经没有了关系

      第二种

    var a = []int{1,2,3,4,5}
    var b = make([]int,5)
    
    copy(b,a) //值拷贝
    b[0] = 12
    log.Println(a,b) //输出:[1 2 3 4 5] [12 2 3 4 5] 

    四、平时使用需要注意的地方

      1、作为函数参数传递(go语言的函数参数传递,只有值传递,没有引用传递),但有时候我们会产生一些误区,看看下面这个例子

    func f(a []int){
        a[0] = 12
        a = append(a,6)
    }
    
    func main(){
        var a = []int{1,2,3,4,5}
        f(a)
        log.Println(a) //输出:[12 2 3 4 5] 第一个元素改变了,但值6并没有追加到a中
    }

       2、range 中改变值

    var a = []int{1, 2, 3, 4, 5}
    
    for _, v := range a { //不会改变a中元素的值(v只是一个临时变量)
        v = v + 2
    }
    log.Println(a) //输出:[1 2 3 4 5]
    
    for i := range a { //会改变a中的值
        a[i] = i + 2
    }
    log.Println(a) //输出:[2 3 4 5 6]

    五、扩展

      1、slice的结构

    func main(){
        var a = []int{1, 2, 3, 4, 5}
        s := (*SliceHeader)(unsafe.Pointer(&a))
    
        log.Println(s.Len,s.Cap) //输出:5 5
    }
    
    type SliceHeader struct {
        Data uintptr    //底层数组地址
        Len  int        //长度
        Cap  int        //容量
    }

      2、append方法必须要有接收者

    var a = []int{1, 2, 3, 4, 5}
    append(a,6) //编译失败

      3、数组的元素在内存中地址是连续的

    var a = []int{1, 2, 3, 4, 5}
    s := (*reflect.SliceHeader)(unsafe.Pointer(&a))
    
    //根据数组索引获取元素值
    arrGet := func(n int)int{
        if n >= s.Len{
            panic("索引越界")
        }
        v := (*int)(unsafe.Pointer(uintptr(unsafe.Pointer(s.Data))+unsafe.Sizeof(int(0))*uintptr(n)))
        return *v
    }
    log.Println(arrGet(0)) //输出:1
    log.Println(arrGet(1)) //输出:2
    log.Println(arrGet(5)) //输出:panic: 索引越界

    以上内容为个人理解,如有问题欢迎指出。

  • 相关阅读:
    在 easyui中获取form表单中所有提交的数据 拼接到table列表中
    easyui中清空table列表中数据
    easyui中加载table列表数据 第一次有数据第二次没有数据问题
    jsp中将一个jsp引入另一个jsp指定位置
    maven项目修改名称后,打包名称和现在名称不一致
    动态sql
    日期转化类 ,日期格式处理
    easyui中权限分配和添加 前后端代码
    ubuntu14.04下播放器SMplayer的安装
    C++的 new 和 detele
  • 原文地址:https://www.cnblogs.com/fanxp/p/13769350.html
Copyright © 2020-2023  润新知