数组和切片

数组-array

数组的声明

  1. 数组是同一种数据类型的固定长度的序列

  2. 数组的定义: var a [len]int,比如:var a[6]int,一旦定义,长度不能变

  3. 长度是数组类型的一部分,因此,

    // var a[5] int`和` var a[10] int 是不同的类型
    func example() {
    	var a = [3]int{1, 2, 3}
    	var b = [5]int{1, 2, 3, 4, 5}
    	fmt.Printf("%v,%T\n", a, a) //[1 2 3],[3]int
    	fmt.Printf("%v,%T\n", b, b) //[1 2 3 4 5],[5]int
    }
  4. 数组可以通过下标进行访问,下标是从0开始,最后一个元素下标是:len-1

  5. 访问越界,如果下标在数组合法范围之外,则触发访问越界,会panic

    // 访问越界问题
    func test1() {
    	var a [10]int
    	// j := 10 // 不能超出数组的范围,会报错panic
    	j := 9
    	a[0] = 10
    	a[j] = 100
    	fmt.Println(a)
    
    	for i := 0; i < len(a); i++ {
    		fmt.Println(a[i])
    	}
    	for index, val := range a {
    		fmt.Printf("a[%d]=%d\n", index, val)
    	}
    }
  6. 数组是值类型,因此改变副本的值,不会改变本身的值

//数组是值类型,改变副本的值,不会改变本身的值
func test2() {
	var a [10]int
	b := a   //相等于ctrl+c ctrl+v ==>把world文档从A文件复制到B文件
	b[0] = 101
	fmt.Println(a) //[0 0 0 0 0 0 0 0 0 0] a的值没有发生变化
}

// 如果需要 改变 原来的数组的值需要传入 地址 进去
func test3(arr [5]int) {
	arr[0] = 1000
}
func test4(arr *[5]int) {
	arr[0] = 1000
}
func test41() {
	var a [5]int
	test3(a)
	fmt.Println(a) //[0 0 0 0 0]
	test4(&a)      //&a传入值类型的地址
	fmt.Println(a) //[1000 0 0 0 0]
}

数组的初始化

如果不初始化:默认元素都是零值(布尔值:false, 整型和浮点型都是0, 字符串:"")

var age0 [5]int = [5]int{1, 2, 3} //自动填充0,初始化方式1  [1 2 3 0 0] 
var age1 = [5]int{1, 2, 3, 4, 5}  //初始化方式2   [1 2 3 4 5]
var age2 = [...]int{1, 2, 3, 4, 5} //自动计算长度,初始化方式3 [1 2 3 4 5]
var age3 = [...]int{2:100,5:200} //按照索引进行初始化,初始化方式4 [0 0 100 0 0 200]
var str = [5]string{3: "hello world", 4: "tom"}  //[   hello world tom]

数组的遍历

	var s4 = [...]string{"青铜", "白银", "黄金", "白金"}
//索引遍历
	for i := 0; i < len(s4); i++ {
		fmt.Printf("%s\n", s4[i])
	}
//range遍历
	for i, v := range s4 {
		fmt.Printf("(%d,%s)\n", i, v)
	}

多维数组

	//多维数组定义
	var i6 [3][2]int  //i6的类型是[3]([2]int)类型
	i6 = [3][2]int{ //默认填充[0 0]数组
		[2]int{1, 3},
		[2]int{1, 2},
	}
	fmt.Println(i6) //[[1 3] [1 2] [0 0]]

多维数组遍历

var f [2][5]int = [2][5]int{{1, 2, 3, 4, 5}, {6, 7, 8, 9, 10}} //2行5列
for row, v := range f { //遍历行,v其实也就是一个数组
	for col, v1 := range v { //遍历列
		fmt.Printf("{%d,%d}=%d", row, col, v1) //坐标的值
	}
	fmt.Println()
}
// {0,0}=1{0,1}=2{0,2}=3{0,3}=4{0,4}=5
// {1,0}=6{1,1}=7{1,2}=8{1,3}=9{1,4}=10

切片-slice

  1. 切片是引用类型,都指向了底层的一个数组,切片不保存值,只是内存地址引用的是底层的数组
	var a2 = [5]int{1, 2, 3, 4, 5}
	ss1 := a2[1:5]
	fmt.Println(ss1) //[2 3 4 5]
	a2[3] = 1000 //修改底层数组的值
	fmt.Println(ss1) //[2 3 1000 5] 切片ss1发生变化
  1. 切片的长度是可以改变,因此,切片是一个可变的数组,切片的长度是它元素的个数

  2. 切片遍历方式和数组一样,可以用len()求长度

  3. cap可以求出slice最大的容量,0<=len(slice) <= cap(array),其中array是slice引用的数组

    // 长度和容量
    fmt.Printf("len(s1):%d cap(s1):%d\n", len(s1), cap(s1))
    fmt.Printf("len(s2):%d cap(s2):%d\n", len(s2), cap(s2)
  4. 切片的定义:

切片没有被初始化,默认是nil

// var 变量名 []类型
// 切片的定义
	var s1 []int           //定义一个元素为int类型的切片
	fmt.Println(s1)        //[]
	fmt.Println(s1 == nil) //true
	s2 = []int{1, 2, 3} //给切片添加元素

切片的初始化

基于数组定义切片

	// 由数组得到切片
	a1 := [...]int{1, 3, 5, 7, 9, 11, 13}
	s3 := a1[0:4] // 基于一个数组切割切片,左包含右不包含,(左闭右开)
	fmt.Println(s3)
	s4 := a1[1:6]
	fmt.Println(s4)
	s5 := a1[:4] // => [0:4] [1 3 5 7]
	s6 := a1[3:] // => [3:len(a1)]  [7 9 11 13]
	s7 := a1[:]  // => [0:len(a1)] 
  s8:= a1[:len(a1)-1]// =>去掉最后一个元素
	fmt.Println(s5, s6, s7)

使用make()构造切片

var slice []type=make([]type,len)
slice := make([]type,len)
slice := make([]type,len,cap)

	//make 函数创造切片
	ms1 := make([]int, 5, 10)                         //定义切片类型,长度,容量
	fmt.Printf("%v,%d,%d\n", ms1, len(ms1), cap(ms1)) //[0 0 0 0 0],5,10

切片的len()和cap()

	a4 := [...]int{4, 5, 6, 7, 8, 9, 0}
	s7 := a4[:4] //[4 5 6 7]
	s8 := a4[3:] //[7 8 9 0]
	s9 := a4[:]  //[4 5 6 7 8 9 0]
	//长度和容量
	fmt.Printf("len(s7)=%d,cap(s7)=%d\n", len(s7), cap(s7)) //len(s7)=4,cap(s7)=7
	fmt.Printf("len(s8)=%d,cap(s8)=%d\n", len(s8), cap(s8)) //len(s8)=4,cap(s8)=4
	fmt.Printf("len(s9)=%d,cap(s9)=%d\n", len(s9), cap(s9)) //len(s9)=7,cap(s9)=7

切片的遍历

//切片的遍历和数组相同	
  for i := 0; i < len(ms1); i++ {
		fmt.Println(ms1[i])
	}
	for _, v := range ms1 {
		fmt.Println(v)
	}

切片可以再次切片

var a = []int {1,2,3,4,5}
b := a[1:2]
b = b[0:3]

切片的本质

切片的本质就是对底层的数组的封装。包含3个信息:底层数组的指针切片的长度切片的容量

  • 切片就是一个框,框住了一块连续的内存。
  • 切片属于引用类型,真正的数据都是保存在底层数组里的。
  • 判断一个切片是否是空的,要是用len(s) == 0来判断
a:=[8]int{1,2,3,,4,5,6,7}
s1 := a[:5]

image-20190707182623280

s2 := a[3:6]

image-20190707182736535

用append内置函数操作切片

  • append为切片追加元素,如果容量满了可以自动扩容
  • 使用append函数,必须要用原来的切片变量来接收返回值
  • append可以追加一个新的切片到原来的切片中,使用...展开切片
	//append函数为切片追加元素
	as1 := []string{"青铜"}
	as1 = append(as1, "砖石") //使用append函数,必须要用原来的切片变量来接收返回值
	fmt.Println(as1)
	as1 = append(as1, "王者", "白金")
	ss11 := []string{"白银", "黄金"}
	as1 = append(as1, ss11...) //append追加一个切片到原来的切片中
	fmt.Println(as1) //[青铜 砖石 王者 白金 白银 黄金]

关于append删除切片中的某个元素

	aa1 := [...]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 5, 4}
	aas1 := aa1[:]

	aas1 = append(aas1[:2], aa1[3:]...) //给切片aas1[:2]追加元素为一个切片aa1[3:]

	fmt.Println(aas1) //[1 2 4 5 6 7 8 9 5 4]
	fmt.Println(aa1) //[1 2 4 5 6 7 8 9 5 4 4]

切片的拷贝-内置函数copy()

  • copy 是把原来的切片里面的全部元素拷贝到另一个切片里面,此时俩个切片之间没有任何关系
	//copy()的使用,copy的是切片底层的数组,copy完成后,会变成俩个完全独立的切片,也就是深拷贝
	//copy(目标,原)
	ca1 := []int{1, 3, 5}
	ca2 := ca1                  //切片赋值
	var ca3 = make([]int, 5, 6) //make一个长度为5,容量为6的切片,默认值为[0 0 0 0 0]
	copy(ca3, ca1)              //copy
	fmt.Println(ca1, ca2, ca3)  //[1 3 5] [1 3 5] [1 3 5 0 0]
	ca1[0] = 100
	fmt.Println(ca1, ca2, ca3) //[100 3 5] [100 3 5] [1 3 5 0 0]

string与slice

string底层就是一个byte的数组,因此,也可以进行切片操作

str := "hello world"
s1 := str[0:5]
fmt.Println(s1)

s2 := str[5:]
fmt.Println(s2)

string内存布局

如何改变string中的字符值

string本身是不可变的,因此要改变string中字符,需要如下操作:

func testModifyString() {
	s := "hello world"
	s1 := []rune(s) //按照字符大小格式化

	s1[1] = '0'
	str := string(s1)
	fmt.Println(str)
}

排序和查找操作-sort包

转换为切片才能对数组进行排序

  • sort.Ints对整数进行排序
  • sort.Strings对字符串进行排序
  • sort.Float64s对浮点数进行排序
  • sort.SearchInts(a []int,b int) 从数组a中查找b,前提是a必须有序
  • sort.SearchFloats(a []float64,b float64) 从数组a中查找b,前提是a必须有序
  • sort.SearchStrings(a []string,b string) 从数组a中查找b,前提是a必须有序
package main

import (
	"fmt"
	"sort"
)

// int进行排序
func testIntsort() {
	var a = [...]int{2, 4, 12, 5, 6, 2, 121, 3}
	sort.Ints(a[:])   //转换为切片才能对数组进行排序
	fmt.Println(a)
}

//字符串排序
func testStringSort() {
	var a = [...]string{"abc", "s", "sdfdsf", "xz", "q"}
	sort.Strings(a[:])
	fmt.Println(a)
}

// float排序
func testFloatSort() {
	var a = [...]float64{1.2, 32.1, 4.1, 2, 12, 3.2}
	sort.Float64s(a[:])
	fmt.Println(a)
}
//int类型搜索
func testIntSearch() {
	var a = [...]int{2, 4, 12, 5, 6, 222, 121, 3}
	// sort.Ints(a[:])
	index := sort.SearchInts(a[:], 121) //返回的是排序后的索引位置
	fmt.Println(index)
}
func main() {
	testIntsort()
	testStringSort()
	testFloatSort()
	testIntSearch()
}

array和slice类型的区别

  • array声明的时候,方括号里面需要注明array的长度;slice声明的时候,方括号为空

  • 作为函数参数时,array传递的是一个数组的副本,而slice传递的是一个指针

  • array是值类型,slice是引用类型

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Golang的数组切片是两种不同的数据类型,用于存储相同数据类型的容器。数组的长度是固定的,而切片的长度是可变的。在日常应用中,切片的使用更为普遍。 数组在声明时需要指定长度,并且在初始化时必须提供相同长度的元素。例如,`a := int{1, 2, 3}`就是一个长度为3的整数数组数组的长度一旦确定后就不能更改。 切片是基于数组的引用类型。它不需要指定固定的长度,并且可以根据需要动态扩展或缩小。切片包装着底层数组,通过指定起始索引和结束索引来指定子集。例如,`b := a[:]`就是一个切片,它包含了数组a的所有元素。 数组适用于需要固定长度的场景,而切片适用于长度可变的情况。在实际应用中,切片更常用,因为它提供了更大的灵活性和便利性。 总结: - 数组是长度固定的容器,切片是长度可变的容器; - 数组在声明时需要指定长度,切片则不需要; - 数组的长度一旦确定后就不能更改,而切片可以根据需要动态扩展或缩小; - 切片是基于数组的引用类型,可以通过指定起始索引和结束索引来指定子集。 参考资料: Golang中的「数组」和「切片」都是存储同一数据类型的容器,只不过Golang中的数组长度是固定的,而切片的长度是可变化的。 切片是引用类型,切片包装的数组称为该切片底层数组。我们来看一段代码://a是一个数组,注意数组是一个固定长度的,初始化时候必须要指定长度,不指定长度的话就是切片了 a := int{1, 2, 3} //b是数组,是a...。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值