golang 数组 字符串 切片理解

1、数组
数组是一个由固定长度的特定类型元素组成的序列,一个数组可以由零个或多个元素组成。数组的长度是数组类型的组成部分。因为数组的长度是数组类型的一个部分,不同长度或不同类型的数据组成的数组都是不同的类型,因此在Go语言中很少直接使用数组(不同长度或不同类型元素的数组因为类型不同无法直接赋值)。
如上面所说,因为长度也是数组的类型组成部分,所以即使两个有相同元素类型但长度不同的数组是无法进行赋值的:

var a = [3]int{}
var b = [2]int{}
b = a 

上面代码编译时会出错:cannot use a (type [3]int) as type [2]int in assignment

数组定义方式:

var a [3]int                    // 定义长度为3的int型数组, 元素全部为0
var b = [...]int{1, 2, 3}       // 定义长度为3的int型数组, 元素为 1, 2, 3
var c = [...]int{2: 3, 1: 2}    // 定义长度为3的int型数组, 元素为 0, 2, 3
var d = [...]int{1, 2, 4: 5, 6} // 定义长度为6的int型数组, 元素为 1, 2, 0, 0, 5, 6

Go语言中数组是值语义。一个数组变量即表示整个数组,它并不是隐式的指向第一个元素的指针(比如C语言的数组),而是一个完整的值。当一个数组变量被赋值或者被传递的时候,实际上会复制整个数组。如果数组较大的话,数组的赋值也会有较大的开销。为了避免复制数组带来的开销,可以传递一个指向数组的指针,但是数组指针并不是数组。

var a = [...]int{1, 2, 3} // a 是一个数组
var b = &a                // b 是指向数组的指针

fmt.Println(a[0], a[1])   // 打印数组的前2个元素
fmt.Println(b[0], b[1])   // 通过数组指针访问数组元素的方式和数组类似

for i, v := range b {     // 通过数组指针迭代数组的元素
    fmt.Println(i, v)
}

其中b是指向a数组的指针,但是通过b访问数组中元素的写法和a类似的。还可以通过for range来迭代数组指针指向的数组元素。其实数组指针类型除了类型和数组不同之外,通过数组指针操作数组的方式和通过数组本身的操作类似,而且数组指针赋值时只会拷贝一个指针。但是数组指针类型依然不够灵活,因为数组的长度是数组类型的组成部分,指向不同长度数组的数组指针类型也是完全不同的。

可以将数组看作一个特殊的结构体,结构的字段名对应数组的索引,同时结构体成员的数目是固定的。内置函数len可以用于计算数组的长度,cap函数可以用于计算数组的容量。不过对于数组类型来说,len和cap函数返回的结果始终是一样的,都是对应数组类型的长度。

初始化一个数组后,其长度就不能再进行变动了,但其内容可以改变。

2、字符串
一个字符串是一个不可改变的字节序列,字符串通常是用来包含人类可读的文本数据。和数组不同的是,字符串的元素不可修改,是一个只读的字节数组。每个字符串的长度虽然也是固定的,但是字符串的长度并不是字符串类型的一部分
因为字符串长度并不是字符串类型, 所以字符串之间可以相互赋值。

3、切片
简单地说,切片就是一种简化版的动态数组。因为动态数组的长度是不固定,切片的长度自然也就不能是类型的组成部分了。数组虽然有适用它们的地方,但是数组的类型和操作都不够灵活,因此在Go代码中数组使用的并不多。而切片则使用得相当广泛,理解切片的原理和用法是一个Go程序员的必备技能。
切片定义方式:

var (
    a []int               // nil切片, 和 nil 相等, 一般用来表示一个不存在的切片
    b = []int{}           // 空切片, 和 nil 不相等, 一般用来表示一个空的集合
    c = []int{1, 2, 3}    // 有3个元素的切片, len和cap都为3
    d = c[:2]             // 有2个元素的切片, len为2, cap为3
    e = c[0:2:cap(c)]     // 有2个元素的切片, len为2, cap为3
    f = c[:0]             // 有0个元素的切片, len为0, cap为3
    g = make([]int, 3)    // 有3个元素的切片, len和cap都为3
    h = make([]int, 2, 3) // 有2个元素的切片, len为2, cap为3
    i = make([]int, 0, 3) // 有0个元素的切片, len为0, cap为3
)

切片的两个重要概念:
⭕Len(长度, 切片中有效元素的长度)
⭕Cap(容量, 容量必须大于或等于切片的长度)

var a = make([]int, 0, 5)
fmt.Println(len(a)) // 输出结果:0
fmt.Println(cap(a)) // 输出结果:5

添加切片元素:

var a []int                       // len、cap都为0
a = append(a, 1)                  // 追加1个元素
a = append(a, 1, 2, 3)            // 追加多个元素, 手写解包方式
a = append(a, []int{1,2,3}...)    // 追加一个切片, 切片需要解包
a = append([]int{0}, a...)        // 在开头添加1个元素
a = append([]int{-3,-2,-1}, a...) // 在开头添加1个切片

在容量不足的情况下,append的操作会导致重新分配内存,可能导致巨大的内存分配和复制数据代价。即使容量足够,依然需要用append函数的返回值来更新切片本身,因为新切片的长度已经发生了变化
在代码中,定义切片时尽量使容量充足,避免增添元素时需要频繁的重新分配内存,以提高代码效率。
切片本身不包含任何数据,它仅仅是底层数组的一个上层表示,对切片进行的任何修改都将反映在底层数组中。看如下示例:

a := []int{1, 2, 3, 4, 5}
s := a[1:4]
for i:=0; i<len(s);i++ {
  s[i] ++
}
fmt.Println(a) // [1 3 4 5 5]
fmt.Println(s) // [3 4 5]

参考文档

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值