Go中的数组与切片

2022-5-15 17:38| 发布者: ugfet92vl4va2v| 查看: 1620| 评论: 0

这里是默认签名
容器类型

java

java中的容器类型常用的是List,Set,HashMap等。
在java中谈容器,一般指的是Collection和Map。数组不属于容器的范围。



但是go中我们说到容器类型,一般是说数组、切片和map
go的数组

go数组的两个特性:长度固定,元素类型相同。
var arrName [n]T    //长度为n,类型是T[n]T 是arrName的数组类型。也就是说如果两个数组类型的元素类型 T 与数组长度 N 都是一样 的,那么这两个数组类型是等价的,如果有一个属性不同,它们就是两个不同的数组类 型。
因为数组在定义时其类型和长度都是明确的,所以实际内存分配上,也是一块连续的,可容纳所有数据的内存。
     // 数组的声明     var a [3]int // 默认初始化为int的零值     a[0] = 1      b := [3]int{1, 3, 5}           // 声明同时初始化     c := [2][2]int{{1, 1}, {2, 2}} //多维数组      d := [...]int{1, 2, 3, 4, 5} // 不用写数组的长度          var e = [...]int{ // 稀疏数组         99: 39, // 将第100个元素(下标值为99)的值赋值为39,其余元素值均为0         }Go 提供了预定义函数 len 可以用于获取一 个数组类型变量的长度,通过 unsafe 包提供的 Sizeof 函数,我们可以获得一个数组变量 的总大小
     t.Log(len(d))  // 5     t.Log(unsafe.Sizeof(d)) // 40切片slice

切片的定义和数组很像,仅仅是少了一个“长度”属性。切片的存在是为了解决数组的问题,数组长度固定,很不灵活。
var myslice = []int{1, 2, 3, 4, 5, 6}使用内置的append函数添加元素
myslice = append(myslice, 100)slice的实现

slice的底层结构是
type slice struct {     array unsafe.Pointer // 指向底层数组的指针     len int  // 切片的长度,即切片中当前元素的个数;     cap int  // 底层数组的长度,也是切片的最大容量,cap 值永远大于等于 len 值 }每个新建的slice都会新建一个底层数组。数组的长度和切点初始元素的个数相同。
我们还有其他方法创建切片。

  • 通过make 函数来创建切片,并指定底层数组的长度 c := make([]int, 3, 5) // 切点的len是3,cap是5,即底层数组的长度是5.如果不指定。默认cap = len
    t.Log(len(c), cap(c))


  • 在已有数组的基础上创建切片
  • 采用 array[low : high : max]语法基于一个已存在的数组创建切片。这种方式被 称为数组的切片化 func TestArr2Slice(t *testing.T) {
    month := [12]string{"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}
    slc := month[3:6:9]
    t.Log(slc) // [Apr May Jun]
    t.Log(len(slc), cap(slc)) // 3 6
    }
    len = high - lowcap = max - low。通常省略max,max默认是数组的长度注意1,现在这个切片slc是直接指向数组month的。也就是说slc的改变会直接改变数组month slc[0] = "4月"
    t.Log(month) // [Jan Feb Mar 4月 May Jun Jul Aug Sep Oct Nov Dec]
    注意2,对一个数组可以创建多个切片。因为这些切片底层都是指向数组的。所以任意一个切片的改变都会影响其他切片。 slc2 := month[3:6]
    t.Log(slc2) // [4月 May Jun]
    t.Log(len(slc2), cap(slc2)) // 3 9
    slc2[1] = "5月"
    t.Log(month) // [Jan Feb Mar 4月 5月 Jun Jul Aug Sep Oct Nov Dec]
    t.Log(slc) // [4月 5月 Jun]

  • 基于切片创建切片用法和基于数组创建切片一样,底层指向同一个数组,所以互相影响。
slice的动态扩容

slice相比array的特点就是不定长。当len == cap时,再对slice进行append,就会发生切片的动态扩容。
// 切片的容量是翻倍增加 func TestSliceGrowing(t *testing.T) {     s := []int{}     for i := 0; i < 20; i++ {         s = append(s, i)         t.Log(len(s), cap(s))     } } 结果打印:     slice_test.go:49: 1 1     slice_test.go:49: 2 2     slice_test.go:49: 3 4     slice_test.go:49: 4 4     slice_test.go:49: 5 8     slice_test.go:49: 6 8     slice_test.go:49: 7 8     slice_test.go:49: 8 8     slice_test.go:49: 9 16     slice_test.go:49: 10 16     slice_test.go:49: 11 16     slice_test.go:49: 12 16     slice_test.go:49: 13 16     slice_test.go:49: 14 16     slice_test.go:49: 15 16     slice_test.go:49: 16 16     slice_test.go:49: 17 32     slice_test.go:49: 18 32     slice_test.go:49: 19 32     slice_test.go:49: 20 32可以看到slice的容量cap时翻倍增加的。
动态扩容导致的与原数组的分割

前面说了,切片可以从数组创建。切片的修改会直接修改原数组。但是,切片是可以继续追加元素的,那么切片追加元素超出了原数组的最大边界会怎么样呢?
func TestArr2SliceOut(t *testing.T) {     month := [12]string{"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}     slc := month[3:6:9]     t.Log(slc)  // [Apr May Jun]     t.Log(len(slc), cap(slc))  // 3 6     slc = append(slc, "7月")     t.Log(slc)    // [Apr May Jun 7月]     t.Log(month)  // [Jan Feb Mar Apr May Jun 7月 Aug Sep Oct Nov Dec]     slc = append(slc, "8月")     slc = append(slc, "9月")     slc = append(slc, "10月")     t.Log(slc)  // [Apr May Jun 7月 8月 9月 10月]     t.Log(month)// [Jan Feb Mar Apr May Jun 7月 8月 9月 Oct Nov Dec] }还是用月份举例。slc切片出了4,5,6三个月。len是3,cap是6。
追加一个7月。此时还在slc的容量范围内,所以直接影响了原数组。
但是当追加到10月时,已经超出了slc的容量范围。此时会进行扩容。slc的扩容会创建一个新的数组,与原数组不在有关系。所以10月不会影响原数组。之后slc做的任何操作都与原数组无关。
同样的道理推广到多个切片指向同一个数组。当某一个切片发生扩容后,他便于其他切片不在指向同一数组,也就不会再相互影响了。
切片的扩容这里是经常埋坑的地方。
一定要清楚的认识到slice与底层数组的关系
这里是默认签名
回复

使用道具 举报

上一篇:做人做事故事篇(之二十三):像小时候父母对待我们那样对待父母

下一篇:「圈中事」我的2018年年终总结

sitemap.txt | sitemap.xml | sitemap.html |Archiver|手机版|小黑屋|彩虹邦人脉系统 ( 皖ICP备2021012059号 )

GMT+8, 2024-11-22 23:02 , Processed in 0.216703 second(s), 62 queries .

快速回复 返回顶部 返回列表