Go数组和切片

一、内部实现

  1. 数组
    数组是一个长度固定的数据类型,用于存储一段具有相同类型的元素的连续块。数组存储的类型可以是内置类型,如整型或者字符串,也可以是某种结构类型。
  2. 切片
    切片是围绕动态数组的概念构建的,可以按需自动增长和缩小
    切片是一个很小的对象,对底层数组进行了抽象,并提供了相关的操作方法。切片有3个字段分别是指向底层数组的指针切片访问的元素个数(即长度)切片允许增长到的元素个数(即容量)

二、区别

2.1、声明和初始化

  1. 数组

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
       //声明一个包含5个元素的整型数组,并设置为零值
    var array [5]int
    //使用数组字面量声明数组
    array := [5]int{10,20,30,40,50}
    //隐式声明数组长度
    array := [...]int{10,20,30,40,50}
    //声明数组并指定特定元素
    array := [5]int{0:10,2:30}

    //使用make
    s2 = make([]int, 16)
    s3 = make([]int, 10, 32)
    printSlice(s2)
    printSlice(s3)
    // len=16, cap=16
    // len=10, cap=32

  2. 切片

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    //使用make
    //长度和容量都是5个元素
    slice := make([]string, 5)
    //长度为3,容量为5个元素
    slice := make([]int, 3, 5)

    //使用字面量声明
    //长度和容量都是4个元素
    slice := []string{"red","blue","green","yellow"}
    //声明切片 并指定特定元素 长度容量都是100
    slice := []string{99:"!!"}

2.2、赋值

  1. 数组

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    func main() {
    var array1[5]string
    array2:=[5]string{"Red","Blue","Green","Yellow","Pink"}

    //把array2的值复制到array1
    array1=array2
    array1[0] = "Black"
    fmt.Println(array1)
    fmt.Println(array2)
    }

如图数组赋值仅仅是复制数组的值

  1. 切片
1
2
3
4
5
6
7
func main() {
slice := []int{1,2,3,4,5}
slice2 := slice
slice2[1] = 100
fmt.Println(slice)
fmt.Println(slice2)
}

切片赋值,仅仅是复制切片的指针、长度和容量

2.3、遍历

  1. 数组的遍历
1
2
3
4
5
6
7
8
9
10
11
func arrRange() {
arr1 := [...]int{2, 4, 6, 8, 10}
//传统方法
for i := 0; i < len(arr1); i++ {
fmt.Println(arr1[i])
}
//range方法
for _, value := range arr1 {
fmt.Println(value)
}

2.4、值传递 && 引用传递

2.4.1、数组变量

1. 值传递

GO中数组就是一个变量 –> 不同大小就是不同类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
func printArray(arr [5]int) {
arr[0] = 100
for _, v := range arr {
println(v)
}
}

func main() {
arr1 := [5]int{1, 2, 3, 4, 5}
arr2 := [3]int{1, 2, 3}
//传递成功
printArray(arr1)
//失败
printArray(arr2)
}

错误:

1
cannot use arr2 (type [3]int) as type [5]int in argument to printArray

  • 第一个成功

  • 第二个不成功,类型不匹配

    1. 数组值修改 – 引用传递
1
2
3
4
5
6
7
8
9
10
11
12
13
func printArray2(arr *[5]int) {
arr[0] = 100
for _, v := range arr {
println(v)
}
}

func main() {
arr1 := [5]int{1, 2, 3, 4, 5}
printArray(arr1)
printArray2(&arr1)
fmt.Println(arr1)
}

2.4.2、切片

Slice 本身没有数据,只是对底层 array 的一个 view,其截取大小只是修改了切片中的ptr和len但是cap为从当前ptr算起到,原切片长度 ^4ae9ee

  • slice 可以向后扩展,不可以向前扩展

  • s[i]不可以超越len(s),向后扩展不可以超越cap(s)
    如图:
    ^a7c516

    1. 指针引用传递

切片截取:

1
2
3
4
5
6
7
func main() {
arr := [...]int{0, 1, 2, 3, 4, 5, 6, 7}
fmt.Println("arr[2:6] = ", arr[2:6])
fmt.Println("arr[2:6] = ", arr[:6])
fmt.Println("arr[2:6] = ", arr[2:])
fmt.Println("arr[2:6] = ", arr[:])
}

输出:

1
2
3
4
arr[2:6] =  [2 3 4 5]
arr[2:6] = [0 1 2 3 4 5]
arr[2:6] = [2 3 4 5 6 7]
arr[2:6] = [0 1 2 3 4 5 6 7]

切片数据修改:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
func updateSlice(s []int) {
s[int] = 100
}

func main() {
arr := [...]int{0, 1, 2, 3, 4, 5, 6, 7}
s1 := arr[2:]
s2 := arr[:]
fmt.Println(s2) // [0 1 2 3 4 5 6 7]
fmt.Println("After updateSlice")
updateSlice(s1)
fmt.Println(s1) // [100 3 4 5 6 7]
fmt.Println(arr) // [0 1 100 3 4 5 6 7]
}

越界问题:

  1. 例一:✅

    1
    2
    3
    4
    5
    arr = [...]int{0,1,2,3,4,5,6,7}
    s1 = arr[2:6]
    s2 = s1[3:6]
    fmt.Println("s1 = ",s1)
    fmt.Println("s2 = ",s2)

    结果:

    1
    2
    s1 =  [2 3 4 5]
    s2 = [5 6 7]
  2. 例2:❌

    1
    fmt.Println(s1[4])

    结果:

    1
    panic: runtime error: index out of range

    原因:[[#^4ae9ee]]

2.5、扩容

2.5.1、切片扩容 Append

1
2
3
4
5
6
7
8
9
arr := [...]int{0, 1, 2, 3, 4, 5, 6, 7}
s1 := arr[2:6]
s2 := s1[3:5]
s3 := append(s2, 10)
s4 := append(s3, 11)
s5 := append(s4, 12)
fmt.Println(s1, s2, s3, s4, s5)

// [2 3 4 5] [5 6] [5 6 10] [5 6 10 11] [5 6 10 11 12]

capacity满了才扩容,每次扩容两倍,所以和java一样,数组最好知道容量,上来就建好。

2.5.1、数组扩容

只能复制