数组与切片

yc90s · 2017-11-21 12:36:16 · 994 次点击 · 预计阅读时间 3 分钟 · 大约8小时之前 开始浏览    
这是一个创建于 2017-11-21 12:36:16 的文章,其中的信息可能已经有所发展或是发生改变。

原文链接

##数组

定义

数组是一组同类型数据的集合,它是值类型,通过从0开始的下标索引访问元素值。初始化后数组长度是固定的,无法修改其长度。当作为方法的入参传入时將复制一份数组而不是引用同一指针。数组的长度也是其类型的一部分。

初始化

  • 长度为5的数组,其元素值依次为:1, 2, 3, 4, 5
    [5]int{1, 2, 3, 4, 5}
    
  • 长度为5的数组,其元素值依次为:1, 2, 0, 0, 0. 在初始化时没有指定初值的元素將会赋值为其元素类型的默认值,int是0,string是““
    [5]int{1, 2}
    
  • 长度为5的数组,其长度是根据初始化时指定的元素个数决定的
    [...]int{1, 2, 3, 4, 5}
    
  • 长度为5的数组,其元素值依次为:0, 0, 1, 2, 3. 在初始化时指定了2, 3, 4索引中对应的值:1, 2, 3
    [5]int{2:1, 3:2, 4:3}
    
  • 长度为5的数组,其元素值为:0, 0, 1, 0, 3. 由于指定了最大索引4对应的值为3, 根据初始化的元素个数确定其长度为5
    [...]int{2:1, 4:3}
    

切片

定义

切片基于数组构建,但是提供更强的功能。切片的存储结构依次是底层数组的指针、切片长度、切片容量、切片数据。切片长度是指已经被赋过值的最大下标+1, 可通过len()获取。容量是指切片目前可容纳的最多元素个数,可通过cap()获取。切片是引用类型,而且切片的长度和容量可以修改,当切片的容量发生变化时,可能会重新分配内存,使得切片和原底层数组的关联断开

初始化

  • 直接初始化,[]表示是切片类型,初始化值依次是1, 2, 3. 其cap=len=3
    s := []int{1, 2, 3}
    
  • 通过数组或其它切片初始化,是其的引用
    s := arr[startIndex:endIndex]
    
  • 通过make()初始化,可指定len和cap
    s := make([]int, len, cap)
    
    切片重新分配内存,导致切片和原底层数组关联断开的情况需要特殊注意,考虑如下代码:
    arr1 := [3]int{1, 2, 3}
    arr2 := [3]int{1, 2, 3}
    sli1 := arr1[:1]
    sli2 := arr2[:1]
    sli1[0] = 0
    sli2[0] = 0
    sli1 = append(sli1, 5)
    sli2 = append(sli2, 5, 5, 5)
    fmt.Println("arr1: ", arr1)
    fmt.Println("arr2: ", arr2)
    fmt.Println("sli1: ", sli1)
    fmt.Println("sli2: ", sli2)
    
    执行后的输出结果是

arr1: [0 5 3]

arr2: [0 2 3]

sli1: [0 5]

sli2: [0 5 5 5]

对切片sli1的append操作实际并没有重新分配内存,它还是原数组的引用,所以对sli1的append操作仍而应用到了数组arr1上;而对切片sli2的append操作,重新分配了内存,使得它和原数组的关联断开了,所以对sli2的append操作并没有影响到数组arr2

数组与切片区别

数组长度不可变,切片长度和容量都可变

数组的长度在初始化时就会确定下来,而切片的长度可以动态变化

数组是值类型,而切片是引用类型

用一段代码说明一下

func testArr(arg [3]int) {
    arg[0] = 0
    fmt.Println("testArr: ", arg)
}

func testSli(arg []int) {
    if len(arg) == 0 {
        arg = append(arg, 0)
    } else {
        arg[0] = 0
    }
    fmt.Println("testSli: ", arg)
}

func main() {
    arr := [3]int{1, 2, 3}
    fmt.Println("before testArr(): ", arr)
    testArr(arr)
    fmt.Println("after testArr(): ", arr)
    sli := arr[:]
    fmt.Println("befor testSli(): ", sli)
    testSli(sli)
    fmt.Println("after testSli(): ", sli)
}

执行后程序输出

before testArr(): [1 2 3]

testArr: [0 2 3]

after testArr(): [1 2 3]

befor testSli(): [1 2 3]

testSli: [0 2 3]

after testSli(): [0 2 3]

testArr()函数对传进去的arr参数的修改,并没有对arr变量生效,而testSli()函数对sli变量的修改写回到了sli变量


有疑问加站长微信联系(非本文作者))

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

994 次点击  
加入收藏 微博
下一篇:golang API
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传