Go数组切片

数组:一组数组

什么是数组

  • 一组数
    • 数组需要是相同类型的数据的集合
    • 数组是需要定义大小的
    • 数组一旦定义了大小是不可以改变的。

数组的声明

package main

import "fmt"

// 数组
// 数组和其他变量定义没什么区别,唯一的就是这个是一组数,需要给一个大小  [6]int   [10]string
// 数组是一个相同类型数据的==有序==集合,通过下标来取出对应的数据
// 数组几个特点:
// 1、长度必须是确定的,如果不确定,就不是数组,大小不可以改变
// 2、元素必须是相,同类型不能多个类型混合, [any也是类型,可以存放任意类型的数据]
// 3、数组的中的元素类型,可以是我们学的所有的类型,int、string、float、bool、array、slice、map

func main() {
   // array数组定义,变量
   // 数组也是一个数据类型
   // 数组的定义:  [数组的大小size]变量的类型 ,
   // 我们定义了一组这个类型的数组集合,大小为size,最多可以保存size个数
   var arr1 [5]int
   // [0,0,0,0,0]
   // 给数组赋值,下标index,所有的数组下标都是从0开始的。
   arr1[0] = 100
   arr1[1] = 200
   arr1[2] = 300
   arr1[3] = 400
   arr1[4] = 500

   // 打印数组
   fmt.Println(arr1)

   // 取出数组中的某个元素
   fmt.Println(arr1[1])

   // 数组中的常用方法 len()获取数组的长度  cap() 获取数组的容量
   fmt.Println("数组的长度:", len(arr1))
   fmt.Println("数组的容量:", cap(arr1))

   // 修改数组的值,index 1 代表的第二个数据了
   arr1[1] = 10
   fmt.Println(arr1)
   fmt.Println(arr1[1])

}

初始化数组的几种方式

package main

import "fmt"

// 数组的赋值初始化
func main() {
	// 在定义数组的时候就直接初始化
	var arr1 = [5]int{1, 2, 3, 4, 5}
	fmt.Println(arr1)

	// 快速初始化 :=
	arr2 := [5]int{1, 2, 3, 4, 5}
	fmt.Println(arr2)

	// 比较特殊的点
	// 数据如果来自用户,我不知道用户给我多少个数据,数组
	// ... 代表数组的长度
	// Go的编译器会自动根据数组的长度来给 ... 赋值,自动推导长度
	// 注意点:这里的数组不是无限长的,也是固定的大小,大小取决于数组元素个数。
	var arr3 = [...]int{1, 2, 3, 4, 5, 5, 6, 7, 8, 8, 8}
	fmt.Println(len(arr3))
	fmt.Println(arr3)

	// 数组默认值,我只想给其中的某几个index位置赋值。
	// {index:值}
	var arr4 [10]int
	arr4 = [10]int{1: 100, 5: 500}
	fmt.Println(arr4) // [0 100 0 0 0 500 0 0 0 0]

}

遍历数组元素

package main

import "fmt"

/*
1、直接通过下标获取元素 arr[index]

2、 0-len i++ 可以使用for循环来结合数组下标进行遍历

3、for range:范围   (new)
*/
func main() {

   var arr1 = [5]int{1, 2, 3, 4, 5}
   fmt.Println(arr1[0])
   fmt.Println(arr1[1])
   fmt.Println(arr1[2])
   fmt.Println(arr1[3])
   fmt.Println(arr1[4])
   // 错误:index 5 out of bounds [0:5] 数组下标越界
   // 数组的长度只有5,你要取出6个元素,不可能取出
   //fmt.Println(arr1[5])
   fmt.Println("------------------")
   // 获取数组的长度  len()
   // 下标从0开始,不能<=
   for i := 0; i < len(arr1); i++ {
      fmt.Println(arr1[i])
   }
   fmt.Println("------------------")
   // goland 快捷方式 数组.for,未来循环数组、切片很多时候都使用for    range
   // for 下标,下标对应的值  range 目标数组切片
   // 就是将数组进行自动迭代。返回两个值 index、value
   // 注意点,如果只接收一个值,这个时候返回的是数组的下标
   // 注意点,如果只接收两个值,这个时候返回的是数组的下标和下标对应的值
   for _, value := range arr1 {
      fmt.Println(value)
   }

}

数组是值类型

在这里插入图片描述

package main

import "fmt"

// 数组是值类型: 所有的赋值后的对象修改值后不影响原来的对象。
func main() {
   //数组类型的样子 [size]type
   arr1 := [4]int{1, 2, 3, 4}
   arr2 := [5]string{"kuangshen", "xuexiangban"}
   fmt.Printf("%T\n", arr1) // [4]int
   fmt.Printf("%T\n", arr2) // [5]string

   // 数组的值传递和int等基本类型一致
   arr3 := arr1
   fmt.Println(arr1)
   fmt.Println(arr3)

   // 修改arr3观察arr1是否会变化
   arr3[0] = 12
   fmt.Println(arr1)
   fmt.Println(arr3) // 数组是值传递,拷贝一个新的内存空间
}

数组的排序

arr := [6]int{1,2,3,4,5,0}
// 升序 ASC  : 从小到大  0,1,2,3,4,5   A-Z    00:00-24:00
// 降序 DESC : 从大到小  5,4,3,2,1,0

数组的排序,一组数是乱序的,我们如何将它按照升序或者降序排列。

排序算法:冒泡排序、插入排序、选择排序、希尔、堆、快排…

数据结构:数组就是最简单的数据结构之一

算法:冒泡排序

package main

import "fmt"

// 冒泡:每次筛选出一个最大或者最小的数.
/*
index   0   1   2   3   4
value   12  99  79  48  55
*/
// 冒泡排序逻辑,两两比较,大的往后移或者前移。 大
// 第一轮 : 12 79 48 55 99 // 5
// 第二轮 : 12 48 55 79 99 // 4
// 第三轮 : 12 48 55 79 99 // 3 //
// 第四轮 : 12 48 55 79 99 //
// 第五轮 : 12 48 55 79 99

// 代码实践
/*
   // 两个数判断,如果一个数大,则交换位置,大放到后面
   if arr[x] > arr[x+1] {
      arr[x], arr[x+1] = arr[x+1],arr[x]
   }
   // 多轮判断,for, 循环次数 【数组大小】
*/
func main() {

   arr := [...]int{12, 99, 79, 48, 55, 1, 110, 111, 23, 52, 354, 2, 3412, 3, 12, 31}
   fmt.Println("初始数组:", arr)
   // 冒泡排序
   // 1、多少轮
   for i := 1; i < len(arr); i++ {
      // 2、筛选出来最大数字以后,我们下次不需要将它再计算了
      for j := 0; j < len(arr)-i; j++ {
         // 比较 // 改变升降序只需要改变符号即可
         if arr[j] < arr[j+1] {
            arr[j], arr[j+1] = arr[j+1], arr[j]
         }

      }
      // 作业:排序已经结束,如果有个机制可以判断已经结束了,不需要再往后了。
      // if xxx {
      //  break
      // }
      fmt.Println("第", i, "交换:", arr)
   }

}

多维数组(简单的)

一维数组: 线性的,一组数

二维数组: 表格性的,数组套数组

三维数组: 立体空间性的,数组套数组套数组

xxxx维数组:xxx,数组套数组套数组…
在这里插入图片描述

三维数组,x,y,z,立体空间一样的。
在这里插入图片描述

package main

import "fmt"

func main() {

   // 定义一个多维数组  二维

   arr := [3][4]int{
      {0, 1, 2, 3},   // arr[0]  //数组
      {4, 5, 6, 7},   // arr[1]
      {8, 9, 10, 11}, // arr[2]
   }
   // 二维数组,一维数组存放的是一个数组
   fmt.Println(arr[0])
   // 要获取这个二维数组中的某个值,找到对应一维数组的坐标,arr[0] 当做一个整体
   fmt.Println(arr[0][1])
   fmt.Println("------------------")
   // 如何遍历二维数组
   for i := 0; i < len(arr); i++ {
      for j := 0; j < len(arr[i]); j++ {
         fmt.Println(arr[i][j])
      }
   }
   // for range
   for i, v := range arr {
      fmt.Println(i, v)
   }
}

切片:对数组的一个抽象Slice

Go 语言切片是对数组的抽象。

Go 数组的长度不可改变,在特定场景中这样的集合就不太适用,Go 中提供了一种灵活,功能强悍的内置类型 切片(“动态数组”),与数组相比切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大

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

切片与数组相比,不需要设定长度,在[]中不用设定值,相对来说比较自由

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

  • 指针:指向数组中 slice 指定的开始位置
  • 长度:即slice的长度
  • 最大长度:也就是 slice 开始位置到数组的最后位置的长度

定义切片

package main

import "fmt"

// 定义切片
func main() {

   arr := [4]int{1, 2, 3, 4} // 数组定长
   fmt.Println(arr)

   var s1 []int // 变长,长度是可变的
   fmt.Println(s1)
   // 切片的空判断,初始的切片中,默认是 nil
   if s1 == nil {
      fmt.Println("切片是空的")
   }

   s2 := []int{1, 2, 3, 4} // 切片 变长
   fmt.Println(s2)
   fmt.Printf("%T,%T\n", arr, s2) // [4]int,[]int
   fmt.Println(s2[1])
}

make来创建切片

package main

import "fmt"

func main() {

   // make()
   // make([]Type,length,capacity) // 创建一个切片,长度,容量
   s1 := make([]int, 5, 10)
   fmt.Println(s1)
   fmt.Println(len(s1), cap(s1))
   // 思考:容量为10,长度为5,我能存放6个数据吗?
   s1[0] = 10
   s1[7] = 200 // index out of range [7] with length 5
   // 切片的底层还是数组 [0 0 0 0 0] [2000]
   // 直接去赋值是不行的,不用用惯性思维思考
   fmt.Println(s1)
   
   // 切片扩容

}

切片扩容

package main

import "fmt"

func main() {
   s1 := make([]int, 0, 5)
   fmt.Println(s1)
   // 切片扩容,append()
   s1 = append(s1, 1, 2)
   fmt.Println(s1)
   // 问题:容量只有5个,那能放超过5个的吗? 可以,切片是会自动扩容的。
   s1 = append(s1, 2, 3, 3, 4, 4, 5, 6, 6, 7, 7)
   fmt.Println(s1)
   
   // 切片扩容之引入另一个切片。
   // new : 解构   slice.. ,解出这个切片中的所有元素。
   s2 := []int{100, 200, 300, 400}
   // slice = append(slice, anotherSlice...)
   // ... 可变参数 ...xxx
   // [...] 根据长度变化数组的大小定义
   // anotherSlice... , slice...解构,可以直接获取到slice中的所有元素
   // s2... = {100,200,300,400}
   s1 = append(s1, s2...)
}

遍历切片

package main

import "fmt"

func main() {
	s1 := make([]int, 0, 5)
	fmt.Println(s1)
	// 切片扩容,append()
	s1 = append(s1, 1, 2)
	fmt.Println(s1)
	// 问题:容量只有5个,那能放超过5个的吗? 可以,切片是会自动扩容的。
	s1 = append(s1, 2, 3, 3, 4, 4, 5, 6, 6, 7, 7)
	fmt.Println(s1)

	// 切片扩容之引入另一个切片。
	// new : 解构   slice.. ,解出这个切片中的所有元素。
	s2 := []int{100, 200, 300, 400}
	// slice = append(slice, anotherSlice...)
	// ... 可变参数 ...xxx
	// [...] 根据长度变化数组的大小定义
	// anotherSlice... , slice...解构,可以直接获取到slice中的所有元素
	// s2... = {100,200,300,400}
	s1 = append(s1, s2...)

	// 遍历切片
	for i := 0; i < len(s1); i++ {
		fmt.Println(s1[i])
	}

	for i := range s1 {
		fmt.Println(s1[i])
	}
}

扩容的内存分析

1、每个切片引用了一个底层的数组
2、切片本身不存储任何数据,都是底层的数组来存储的,所以修改了切片也就是修改了这个数组中的数据
3、向切片中添加数据的时候,如果没有超过容量,直接添加,如果超过了这个容量,就会自动扩容,成倍的增加, copy
4、切片一旦扩容,就是重新指向一个新的底层数组。

package main

import "fmt"

// 切片扩容的内存分析
// 结论
// 1、每个切片引用了一个底层的数组
// 2、切片本身不存储任何数据,都是底层的数组来存储的,所以修改了切片也就是修改了这个数组中的数据
// 3、向切片中添加数据的时候,如果没有超过容量,直接添加,如果超过了这个容量,就会自动扩容,成倍的增加, copy
//   - 分析程序的原理
//   - 看源码
//
// 4、切片一旦扩容,就是重新指向一个新的底层数组。
func main() {
   // 1、cap 是每次成倍增加的
   // 2、只要容量扩容了,地址就会发生变化
   s1 := []int{1, 2, 3}
   fmt.Println(s1)
   fmt.Printf("len:%d,cap:%d\n", len(s1), cap(s1)) // len:3,cap:3
   fmt.Printf("%p\n", s1)                          // 0xc000016108

   s1 = append(s1, 4, 5)
   fmt.Printf("len:%d,cap:%d\n", len(s1), cap(s1)) // len:5,cap:6
   fmt.Printf("%p\n", s1)                          // 0xc000010390

   s1 = append(s1, 6, 7, 8)
   fmt.Printf("len:%d,cap:%d\n", len(s1), cap(s1)) // len:8,cap:12
   fmt.Printf("%p\n", s1)                          // 0xc00005e060

   s1 = append(s1, 9, 10)
   fmt.Printf("len:%d,cap:%d\n", len(s1), cap(s1)) // len:10,cap:12
   fmt.Printf("%p\n", s1)                          // 0xc00005e060

   s1 = append(s1, 11, 12, 13, 14)
   fmt.Printf("len:%d,cap:%d\n", len(s1), cap(s1)) // len:14,cap:24
   fmt.Printf("%p\n", s1)                          // 0xc00010c000
}

copy

package main

import "fmt"

// copy 方法
func main() {
   numbers := []int{1, 2, 3}
   fmt.Printf("len=%d,cap=%d,slice=%v\n", len(numbers), cap(numbers), numbers)

   // 方法一: 直接使用make创建切片扩容
   numbers2 := make([]int, len(numbers), cap(numbers)*2)
   // 将原来的底层数据的值拷贝到新的数组中
   // func copy(dst, src []Type) int
   copy(numbers2, numbers)

   fmt.Printf("len=%d,cap=%d,slice=%v\n", len(numbers2), cap(numbers2), numbers2)
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值