龙空技术网

Go中的数组与切片

IT技术圈 78

前言:

此刻兄弟们对“java切图工具类”大约比较注重,我们都想要学习一些“java切图工具类”的相关内容。那么小编也在网络上收集了一些对于“java切图工具类””的相关资讯,希望各位老铁们能喜欢,同学们一起来了解一下吧!

容器类型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与底层数组的关系

标签: #java切图工具类