数组
var a [len]intvar a [5]intvar a[5] intvar a[10]intlen-1
for i := 0; i < len(a); i++ {}

for index, v := range a {}
panic[n]*T*[n]T

数组定义

var 数组变量名 [元素数量]Type
var 数组变量名 = [元素数量]Type{元素……}
var 数组变量名 = [……]Type{元素……}
数组变量名 := [……]Type{元素……}
数组变量名 := [元素数量]Type{元素……}

数组遍历

方法1

a := [...]int{1:1, 3:5}
for i := 0; i < len(a); i++ {
	fmt.Print(a[i], " ")
}

方法2

a := [...]int{1:1, 3:5}
for _, value := range a {
    fmt.Print(value, " ")
}

多维数组

// 多维数组
全局:
var 数组名 [行][列]类型
var 数组名 [...][列]类型

局部:
数组名 := [行][列]类型{{第一行},{第二行},...,{第n行}}
数组名 := [...][列]类型{{第一行},{第二行},...,{第n行}} 

// 多维数组遍历
func main() {

    var f [2][3]int = [...][3]int{{1, 2, 3}, {7, 8, 9}}

    for k1, v1 := range f {
        for k2, v2 := range v1 {
            fmt.Printf("(%d,%d)=%d ", k1, k2, v2)
        }
        fmt.Println()
    }
}

数组的内存管理

数组,定长且元素类型一致的数据集合
(1)数组内存是连续的
(2)数组的内存地址实际上就是数组第一个元素的内存地址

数组中的元素是可变的,但长度是固定的,不可更改的

拷贝,,复制+粘贴,,另创一个空间存储拷贝的数组

切片

切片Slice并不是数组或者数组指针,它通过内部指针和相关属性引用数组片段,以实现变长方案

//切片的数据结构定义
type slice struct {
  array unsafe.Pointer
  len int
  cap int
}
//切片的结构体由3部分构成,Pointer是指向一个数组的指针,len代表当前切片的长度,cap是当前切片的容量
len()cap

切片的定义

var 切片名 [] 类型名
// 切片不需要说明长度
// 使用make()函数来创建切片:
var slice [] type = make([]type,len)
简写为
slice := make([]type,len)

// 指定容量,其中capacity为可选参数
slice := make([]T,length,capacity)
// len是数组的长度,并且也是切片的初始长度

切片创建及初始化

// 直接初始化切片,[]表示是切片类型,{1,2,3}初始化依次是1,2,3,其中cap=len=3
s := []int {1,2,3} 

// 初始化切片s,是数组arr的引用
s := arr[:]

// 将数组arr从下标startIndex到endIndex-1的元素创建一个新的切片
s := arr[startIndex:endIndex]

// 默认endIndex时将表示一直到arr的最后一个元素
s := arr[startIndex:]

// 默认startIndex时将表示从arr的第一个元素开始
s := arr[:endIndex]

// 通过切片s初始化切片s1
s1 = s[startIndex:endIndex]

// 通过内置函数make()初始化切片s,[]int标识为其元素类型为int的切片
// make()函数允许在运行期动态指定数组长度,绕开了数组类型必须使用编译期常量的限制
s := make([]int,len,cap)
var s = make([]int,len,cap)
// make函数只用于切片,字典,channel创建

切片的指针类型

var v1 = new([]int)  //创建并初始化,指向默认0
var v2 *[]int //无初始化,指向nil

nil和空切片

nillen(s) == 0s == nil

len()函数、cap()函数

len()cap()nilnilnil 
var s1 []int         //len(s1) = 0;cap(s1) = 0; s1 == nil
s2 := []int{}        //len(s2) = 0;cap(s2) = 0; s2 != nil
s3 := make([]int,0)  //len(s3) = 0;cap(s3) = 0; s1 != nil

append()函数、copy()函数

  • append()函数,增加切片元素
  • copy()函数,拷贝
package main

import "fmt"

func main() {
   var numbers []int
   printSlice(numbers)

   /* 允许追加空切片 */
   numbers = append(numbers, 0)
   printSlice(numbers)

   /* 向切片添加一个元素 */
   numbers = append(numbers, 1)
   printSlice(numbers)

   /* 同时添加多个元素 */
   numbers = append(numbers, 2,3,4)
   printSlice(numbers)

   /* 创建切片 numbers1 是之前切片的两倍容量*/
   numbers1 := make([]int, len(numbers), (cap(numbers))*2)

   /* 拷贝 numbers 的内容到 numbers1 */
   copy(numbers1,numbers)
   printSlice(numbers1)  
}

func printSlice(x []int){
   fmt.Printf("len=%d cap=%d slice=%v\n",len(x),cap(x),x)
}

Slice底层的实现

  • 切片的设计想法是由动态数组概念而来的,为了方便的使一个数据结构自动增加或减少,但是它本身并不是动态数据或者数组指针

  • 在Go中,数组是值类型,赋值和函数传参操作都会复制整个数组数据

  • 因此当数组元素量多的话,会消耗大量的内存,于是,函数传参用数组的指针,也就是说开辟一个空间存放数组,然后设置一个指针指向数组的开端即可,以高效利用内存

  • 但是,传指针有一个弊端,就是当原数组的指针指向更改了,那么函数里的指针指向也会更改

  • 而用切片传数组参数,既可以达到节约内存的目的,也可以达到合理处理好共享内存的问题

  • 故,将一个大数组传递给函数会消耗很多内存,而采用切片的方式传递参数可以避免上述问题

  • 切片是引用传递,所以不需要使用额外的内存并且比使用数组更有效率

  • 切片本身并不是动态数组或者数组指针,其内部实现的数据结构通过指针引用底层数组,设定相关属性将数据读写操作限定在指定的区域内。切片本身是一个只读对象,其工作机制类似数组指针的一种封装

  • 切片是对数组一个连续片段的引用,是一个引用类型,,类似python中的list类型

slice := []int{10,20,30,40,50,60}
// 创建一个len=6,cap=6的切片,同时完成数组里面每个元素值的初始化
// 需要注意的是,[]里面不能写数组的容量,因为如果写了数以后就是数组了,而不再是切片了

image

切片的底层原理
切片的本质是就是一个框,框住了一个连续的内存,只能保存相同类型的元素。
属于引用类型,真正的数据都是保存在底层数组中的

Slice的自动扩容策略

  • 如果新申请的容量比原来容量的2倍大,则新申请的容量就是最终容量;

  • 否则

int