go 语言中的数组array

1.数组的声明:需要指明数组的大小和存储的数据类型

  1. var 数组名 [数组长度] 数组的数据类型 -- 如:var num [128] int

  2. var 数组名 = [数组长度] 数组的数据类型 -- 如:var num = [128] int

可以在声明时赋初值:var num = [4] int{1,2,3,4},不赋初值的默认为0

其他:

var num [4]intnum[0] = 100num[1] = 200num[2] = 300fmt.Println("length:", len(num))fmt.Println("lebngth", cap(num))

2.数组的遍历

使用for循环遍历数组

var num [4]int
num[0] = 100
num[1] = 200
num[2] = 300
num[3] = 400
for i := 0; i < len(num); i++ {
fmt.Println("num = ", num[i])
}

使用for..range来遍历数组

range:指的是范围,不需要操作数组的下标,到达数组的末尾,自动结束for range循环,没出都从数组中获取下标和对应的数值

格式:

for 存放下标的变量,存放数值的变量 := range 数组名{

///

}

如:for index,value := range num {

fmt.Printf("下标是%d,数值是%d\n",index,value)

}

若是你只需要值而希望忽略索引,则可以使用_blank标识符来替换索引来实现这一点

如:

for _,value := range num {

sum += value

}

fmt.Println(sum)

4.数组的排序

冒泡排序:两个循环嵌套,外层执行(长度-1)次,内层执行(长度-i)次,循环体比较相邻两数大就交换。

5.多维数组

跟C语言差不多

定义格式:

var name = [3] [4] int{{1,2,3,4},{5,6,7,8},{9,10,11,12}}

name := [3] [4] int {{1,2,3,4},{5,6,7,8},{9,10,11,12}}

6.其他事项

数组是一个值传递的数据类型,也叫作值类型,值类型传递数据时传递的是数据的副本。

num1 := [2]int{1,2}
num2 := num1
num2[0] = 2 //更改num2中的数据不会影响到num1

每一个数组都有自己的数据类型,当你用%T去判断数组的数据类型时得到的是[size]type这种形式的类型

num := [2]int{2,2}
fmt.Printf("%T",num)//结果是[2]int

切片

什么是切片slice?

首先说一下数组,数组是用来存储一组相同数据类型的数据结构,是定长的

切片与数组类似,不同的是切片是变长的,它的长度是可以改变的,所以也叫作变长数组或者动态数组

切片是一种方便、灵活且强大的包装器,切片本身没有任何数据,他们只是对现有数组的引用。

切片在使用时不需要在[]中指定长度值

从概念上来讲slice像一个结构体,这个结构体中包含了三个元素:

  • 指针,指向数组中slice指定的开始位置

  • 长度,即slice的长度

  • 最大长度,也就是slice开始位置到数组的最后位置的长度

切片的语法

定义切片:

var 切片名 []数据类型

简写:切片名 := []数据类型{数据值}

一般喜欢使用go语言自带的make()函数来创建切片

make(type,len,cap)

type:类型,可选slice、map、chan,创建切片的话可以写 ==> []数据类型

len:长度,实际存储元素的数量

cap:容量,最多能够存储的元素的数量

例:

a := make([]int,3,8)

fmt.Println(a)//输出结果[0 0 0]

从已有数组中创建切片

语法:切片名 := 数组名[开始下标:结束下标]

语法中开始下标和结束下标都可以省略不写,此时分别表示的是从数组头开始和直到数组结尾。

注意,切片取得范围是左闭右开的,如:a[3:8]表示的是从a[3]到a[7]

从已有数组中创建切片,切片的容量是怎么算的呢?

举个例子来说明,若已有一个数组a为{1,2,3,4,5,6,7,8,9,10}

创建的切片为s1 := a[0:5],则该切片指向的地址是a[0]的地址,即数组的首地址,其长度为5,容量为10,容量为10的原因是指向数组的容量为10

创建的切片为s2 := a[3:8],则该切片指向的地址是a[3]的地址,其长度为5,容量为7,容量为7的原因是从a[3]到数组结尾剩余的容量为7

切片的操作

操作切片中数据的方式和数组一样,都是切片名和下标来访问切片中的元素,如slice[0],slice[2]8

向切片的尾部追加元素 append()

slice = append(slice,elem1,elem2)

slice = append(slice,anotherSlice...)

示例:

func main() {
​
  fmt.Println("HelloWorld!")
​
  slice1 := []int{1, 2, 3}
​
  fmt.Println(slice1)
​
  slice2 := make([]int, 0, 8)
​
  fmt.Println(slice2)
​
  slice2 = append(slice2, 1, 2, 3, 4)
​
  fmt.Println(slice2)
​
  slice2 = append(slice2, slice1...)
​
  fmt.Println(slice2)
​
}

结果:

HelloWorld!
[1 2 3]
[]
[1 2 3 4]
[1 2 3 4 1 2 3]

遍历切片和遍历数组的方式一样,不在赘述,贴一个例子

for i := 0; i < len(slice2); i++ {
​
    fmt.Printf("%d\t", slice2[i])
​
  }
​
  fmt.Println()
​
  for _, val := range slice2 {
​
    fmt.Printf("%d\t", val)
​
  }

结果:

1       2       3       4       1       2       3
1       2       3       4       1       2       3

切片的内存分析

切片是一个引用类型的数据类型

1.每一个切片引用了一个底层数组

2.切片本身不存储任何数据,都是这个底层数组存储,随意修改切片也就是修改这个数组中的数据

3.当向切片中添加数据时,如果没有超过容量,直接添加,如果超过了容量则自动扩容(成倍增长:2==》4==》8==》16)(成倍增长!如容量为2,要插入共51个元素,扩容后容量为52)

4.切片一旦扩容,就是重新指向一个新的底层数组

演示实例:

func main() {
    fmt.Println("HelloWorld!")
​
    slice1 := make([]int, 0, 2)
    slice1 = append(slice1, 1, 2)
    fmt.Println(slice1)
    fmt.Printf("len = %d,cap = %d\n", len(slice1), cap(slice1))
​
    slice1 = append(slice1, 3, 4, 5, 6, 7)
    fmt.Println(slice1)
    fmt.Printf("len = %d,cap = %d\n", len(slice1), cap(slice1))
​
    slice1 = append(slice1, 8, 9, 10)
    fmt.Println(slice1)
    fmt.Printf("len = %d,cap = %d\n", len(slice1), cap(slice1))
}

结果

HelloWorld!
[1 2]
len = 2,cap = 2
[1 2 3 4 5 6 7]
len = 7,cap = 8
[1 2 3 4 5 6 7 8 9 10]
len = 10,cap = 16

使用切片的注意点

由于切片是引用类型的数据,所以操作切片的数据时,实质就是在操作所指向底层数组的数据,因此底层数组的数据是会改变的(浅拷贝),若是同时有别的切片指向了这个底层数组,则该切片中的数据也会相应的改变。(此时指的是切片的容量足够的时候,即没有发生扩容)

当切片的容量不足够存放数据时,会拷贝底层数组的数据到一个新的数组(创建后将数据拷贝过去,容量是原数组的倍数)中,再将切片的指针指向新的数组。在这种情况下,是不会对原数组的数据产生影响的。因此若是有别的切片指向了原数组,切片中的数据也不会被改变。

深拷贝和浅拷贝

深拷贝:拷贝的是数据本身

值类型的数据默认都是深拷贝:array,int,float,string,bool,struct

浅拷贝:拷贝的是数据地址

会导致多个变量指向同一块内存

引用类型的数据默认都是浅拷贝:slice,map

go语言内置的函数:copy()将原切片中的元素拷贝到新切片中

func copy(dst,src []type)int

例一:

s1 := []int{1,2,3,4}

s2 := []int{5,6,7}

copy(s1,s2)//将s2的元素拷贝到s1中,注意,这不是追加到s1的末尾而是从第一个元素开始替换掉

fmt.Println(s1)//结果:[5,6,7,4]

fmt.Println(s2)//结果:[5,6,7]

例二:

s1 := []int{1,2,3,4}

s2 := []int{5,6,7}

copy(s2,s1[2:])//将s1中下标2到末尾的元素拷贝到s2中

fmt.Println(s1)//结果:[1,2,3,4]

fmt.Println(s2)//结果:[3,4,7]

例三:

s1 := []int{1,2,3,4}

s2 := []int{5,6,7}

copy(s2[1:],s1)//将s1中的元素拷贝到s2中下标1之后的位置,若是s2的大小不够的话是不会自动扩容的

fmt.Println(s1)//结果:[1,2,3,4]

fmt.Println(s2)//结果:[5,1,2]//s2的容量只有3,所以只拷贝了s1中的前两个元素