Go 编程基础 数组Array 06

数组Array

  • 定义数组的格式: var <varName> [n] <type> ,n> = 0
  • 数组长度也是类型的一部分,因此具有不同长度的数组为不同类型
  • 注意区分指向数组的指针和指针数组
  • 数组在Go为值类型
  • 数组之间可以使用 == 或 != 来进行比较, 但不可以使用< 或 >
  • 可以使用new来创建数组, 此方法放回一个指向数组的指针
  • Go支持多维数组

类型不同不能运算

func main() {
    var a [1]int
    var b [2]int
    //这里的a和b虽然都是数组,但是长度不一样,类型也不一样所以不能运算
    b = a
    fmt.Println(b)
}
# command-line-arguments
./basic_structure.go:32:4: cannot use a (type [1]int) as type [2]int in assignment
修改
func main() {
    var a [1]int
    var b [1]int
    //这里的a和b虽然都是数组,但是长度不一样,类型也不一样所以不能运算
    b = a
    fmt.Println(b)
}
[0]

数组赋值

指定元素个数

因为是整型 又没有默认值

func main() {
    a := [5]int{}
    fmt.Println(a)
}
[0 0 0 0 0]

指定元素个数

因为是字符串 又没有默认值

func main() {
    a := [5]string{}
    fmt.Println(a)
}
[    ]

指定默认值

前三个值是1,2,3 剩下的默认值

func main() {
    a:=[5]int{1,2,3}
    fmt.Println(a)
}
[1 2 3 0 0]

指定key的默认值

第1个key是1 第4个key是5 剩下的默认值

func main() {
    a := [5]int{1: 1, 4: 5}
    fmt.Println(a)
}
[0 1 0 0 5]

不指定数组长度 go自定计算

不指定数组长度 go自定计算

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

不指定长度,结合key定义数组

不指定数组长度 go自定计算

func main() {
    a := [...]int{4: 5}
    fmt.Println(a)
}
[0 0 0 0 5]

指向数组的指针和指针数组的区别

指向数组的指针

分别取出x和y的指针,放到

func main() {
    x, y := 1, 2
    //取出值的指针放到数组
    a := [...]*int{&x, &y}
    fmt.Println(a)
}
[0xc420016090 0xc420016098]

指针数组

func main() {
    a := [...]int{4: 1}
    var p *[5]int = &a
    fmt.Println(p)
}

其实也是输出a的值,,但是前面有个 & 符号.表示取[0 0 0 0 1]数组的地址

&[0 0 0 0 1]

new 关键字创建指针数组

func main() {
    p := new([10]int)
    fmt.Println(p)
}
&[0 0 0 0 0 0 0 0 0 0]

通过a[n]方式修改值

func main() {
    a := [10]int{}
    p := new([10]int)
    a[1] = 1
    p[1] = 1
    fmt.Println(a)
    fmt.Println(p)
}

不管你是用什么方式创建的数组,都是可以通过a[n]=y 的方式来修改数组中的值

[0 1 0 0 0 0 0 0 0 0]
&[0 1 0 0 0 0 0 0 0 0]

指定多维数组

func main() {
    a := [2][3]int{
        {1, 1, 1},
        {2, 2, 2},
    }
    fmt.Println(a)
}
[[1 1 1] [2 2 2]]

最顶级的元素可以通过[...]来自动计算

只有最顶级的元素才可以使用...自动计算

func main() {
    a := [...][3]int{
        {},
        {2, 2, 2},
        {1: 1, 2: 3},
    }
    fmt.Println(a)
}
[[0 0 0] [2 2 2] [0 1 3]]

冒泡排序

func main() {
    a := [...]int{1, 4, 5, 6, 0, 9}
    fmt.Println(a)
    len := len(a)
    for i := 0; i < len; i++ {
        for j := i + 1; j < len; j++ {
            if a[i] < a[j] {
                temp := a[i]
                a[i] = a[j]
                a[j] = temp
            }
        }
    }
    fmt.Println(a)
}
[1 4 5 6 0 9]
[9 6 5 4 1 0]

数组切片Slice

  • 其本身并鄙视数组,它只想底层的数组
  • 作为变长数组的替代方案,可以关联底层数组的局部或者全部
  • 为引用类型
  • 可以直接创建或从底层数组获取生成
  • 使用len()获取元素葛素,cap()获取容量
  • 一般使用make()创建(因为切片本身就是类似于指向指针的数组, 如果用new创建就是一个指向指针的指针,所以不用new )
  • 如果多个slice指向相同底层数组, 其中一个的值改变会影响全部
  • make([]T, len, cap)
  • 其中cap可以省略,则和len的值相同
  • len表示存数的元素个数, cap表示容量

数组方式使用slice

func main() {
    s1 := [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9}
    fmt.Println(s1)
    s2 := s1[5:10] //左开右闭 [6 7 8 9 0]
    s3 := s1[5:] //从第6个取到结尾
    s4 := s1[5:len(s1)] //从第6个取到结尾
    s5 := s1[:5] //取前五个
    fmt.Println(s2)
    fmt.Println(s3)
    fmt.Println(s4)
    fmt.Println(s5)
}
[1 2 3 4 5 6 7 8 9 0]
[6 7 8 9 0]
[6 7 8 9 0]
[6 7 8 9 0]
[1 2 3 4 5]

make声明slice

  • 参数1表示数据类型
  • 参数2表示数据长度
  • 参数3表示数据容量,我们现在是10,如果当数据长度超过10的时候,go会自动的重新分配 10*2 容量大小的内存给我,如此循环
func main() {
    s1 := make([]int, 3, 10)
    fmt.Println(s1)
    fmt.Printf("s1的内存地址 : %p , %v\n", &s1)
}
[0 0 0]
s1的内存地址 : 0xc4200aa020 , %!v(MISSING)

如果不给数据容量,将会是你当前数据的长度

数组reslice

  • Reslice时索引以被slice的切片为准
  • 索引不可以超过被slice的切片的容量cap()值
  • 索引越界不会导致底层数组的重新分配而是引发错误
func main() {
    a:=[]byte{'a','b','c','d','e','f','g','h','i','j','k'}
    //此时 sa 已经是 defghi
    sa := a[3:9]
    //如果我们想要从sa中取出de索引值就就是0:2了..而不是在a中取出的3:5
    sb := sa[0:2]
    fmt.Println(string(sa),len(sa),cap(sa))
    fmt.Println(string(sb),len(sb),cap(sb))
}

数组Append

  • 可以在slice尾部追加元素
  • 可以将一个slice追加在另一个slice尾部
  • 如果最终长度未超过追加到slice的容量则返回原始slice
  • 如果超过追加到slice的容量则将重新分类数组并拷贝原始数据
func main() {
    s1 := make([]int, 3, 6)
    fmt.Printf("%v %p\n", s1, s1)
    s1 = append(s1, 1, 2, 3)
    //追加3个元素,刚刚达到最大容量,所以内存地址是不变的
    fmt.Printf("%v %p\n", s1, s1)
    fmt.Println(cap(s1))
    s1 = append(s1, 1, 2, 3)
    //这里很明显再追加3个元素已经超过最开始定的6个容量,所以内存地址已经变了
    fmt.Printf("%v %p\n", s1, s1)
    //容量也   X2 了
    fmt.Println(cap(s1))
}
[0 0 0] 0xc42001e0f0
[0 0 0 1 2 3] 0xc42001e0f0
6
[0 0 0 1 2 3 1 2 3] 0xc4200180c0
12

func main() {
    a := []int{1, 2, 3, 4, 5}
    s1 := a[2:5]
    s2 := a[1:3]
    fmt.Println(s1, s2)
    //这时候我们改变a中s1和s2共同的一个值3 改为 9
    a[2] = 9
    //也能看到他们都变成了9 这说明.. 他们都是指向a这个数组的
    fmt.Println(s1, s2)
}
[3 4 5] [2 3]
[9 4 5] [2 9]

func main() {
    a := []int{1, 2, 3, 4, 5}
    s1 := a[2:5]
    s2 := a[1:3]
    fmt.Println(s1, s2)
    //我正在这里append了3个元素到s2中,这时候已经超过最大容量了,所以就重新分配了内存快
    s2 = append(s2,1,2,4)
    fmt.Println(cap(s2))
    //因为s2重新分配了内存块,所以这里只会影响到s1,并不会影响到s2
    //如果你的append元素个数没有超过容量的话,还是会影响s2的
    a[2] = 9
    fmt.Println(s1, s2)
}
[3 4 5] [2 3]
8
[9 4 5] [2 3 1 2 4]

copy函数

copy(A,B) 将B拷贝到A中

A个数小于B

这时候就按照数组下标以此替换就好

func main() {
    s1 := []int{1, 2, 3, 4, 5}
    s2 := []int{6, 7, 8, 9, 10, 11, 12, 13, 14}
    copy(s2, s1)
    fmt.Print(s2)
}
[1 2 3 4 5 11 12 13 14]

A个数大于B

这时候就按照数组下标以此替换就好,同时只替换A个元素

func main() {
    s3 := []int{1, 2, 3, 4, 5}
    s4 := []int{6, 7}
    copy(s4, s3)
    fmt.Print(s4)
}
[1 2]

A个数等于B

这个就直接用A替换B

指定A/B的下标位置拷贝

当然不一定都要同时指定

func main() {
    s1 := []int{1, 2, 3, 4, 5}
    s2 := []int{6, 7, 8, 9, 10, 11, 12, 13}
    //这里也可以指定A/B的下标指定拷贝
    copy(s2[0:2], s1[3:5])
    fmt.Print(s2)
}
[4 5 8 9 10 11 12 13]

for range便利slice

func main() {
    //i就是slice的索引 是int 型, 也算一个计数器
    //v就是slice中的值.. v只是slice中的值的一个拷贝,你修改v是不会影响到slice中的值的
    //如果要修改slice中的值你需要用: slice[i] 来改变值
    slice := [...]int{1,2,3,4,5}
    for i, v := range slice {
        fmt.Println(i,v)
    }
}
0 1
1 2
2 3
3 4
4 5