数组可以存放多个同一类型数据。数组也是一种数据类型,在Go中,数组是值类型。
var 数组名 [数组大小]数据类型
var a [5]int// 数组名 [长度]数据类型
赋初值 a[0]= 1 a[1]= 30 .
对上图的总结:
1 ) 数组的地址可以通过数组名来获取 &intArr
2 ) 数组的第一个元素的地址,就是数组的首地址
3 ) 数组的各个元素的地址间隔是依据数组的类型决定,比如int 64 - > 8 int 32 - > 4
package main
import (
"fmt"
)
func main() {
//初始化数组的方式
var numArr01 [3]int = [3]int{1, 2, 3}
fmt.Println("numArr01=", numArr01)
var numArr02 = [3]int{5, 6, 7}
fmt.Println("numArr02=", numArr02)
//这里的 [...] 是规定的写法由go推导数组大小
var numArr03 = [...]int{8, 9, 10}
fmt.Println("numArr03=", numArr03)
var numArr04 = [...]int{1: 800, 0: 900, 2:999}
fmt.Println("numArr04=", numArr04)
f := [...] int{0: 1, 4: 1, 9: 1} // [1 0 0 0 1 0 0 0 0 1]
fmt.Println(f)
e := [5] int{4: 100} // [0 0 0 0 100]
fmt.Println(e)
//类型推导
strArr05 := [...]string{1: "tom", 0: "jack", 2:"mary"}
fmt.Println("strArr05=", strArr05)
}
1)方式1:for(;;;)遍历数组
2)方式2:for-range结构遍历
for index,value :=range array01{
}
1.index数组的下标
2.value该下标对应的值
3.他们都是for循环内可见的局部变量
4.如果不想使用下标index,可以替换为"_"
5.index和value的名称不是固定的。可以自己改变
for-range的案例
package main
import (
"fmt"
)
func main() {
//演示for-range遍历数组
heroes := [...]string{"宋江", "吴用", "卢俊义"}
//使用常规的方式遍历,我不写了..
for i, v := range heroes {
fmt.Printf("i=%v v=%v\n", i , v)
fmt.Printf("heroes[%d]=%v\n", i, heroes[i])
}
for _, v := range heroes {
fmt.Printf("元素的值=%v\n", v)
}
}
1 ) 数组是多个相同类型数据的组合,一个数组一旦声明/定义了,其长度是固定的, 不能动态变化。否则报越界
2 ) 数组中的元素可以是任何数据类型,包括值类型和引用类型,但是不能混用。
3 ) 数组创建后,如果没有赋值,有默认值(零值)
5 ) 使用数组的步骤 1. 声明数组并开辟空间 2 给数组各个元素赋值(默认零值) 3 使用数组
6 ) Go的数组属值类型, 在默认情况下是值传递, 因此会进行值拷贝。数组间不会相互影响
7 ) 如想在其它函数中,去修改原来的数组,可以使用引用传递(指针方式)
package main
import (
"fmt"
)
//函数
func test02(arr *[3]int) {
fmt.Printf("arr指针的地址=%p", &arr)
(*arr)[0] = 88 //!!
}
func main() {
arr := [3]int{11, 22, 33}
fmt.Printf("arr 的地址=%p", &arr)
test02(&arr)
fmt.Println("main arr=", arr)
}
10 ) 长度是数组类型的一部分,在传递函数参数时 需要考虑数组的长度,看下面案例
//题1
package main
import (
"fmt"
)
//默认值拷贝
func modify(arr []int) {
arr[0] = 100
fmt.Println("modify的arr",arr)
}
func main() {
var arr = [...]int{1,2,3}
modify(arr)
}
//编译错误,因为不能把[3]int 传递给[]int
//题2
package main
import (
"fmt"
)
//默认值拷贝
func modify(arr [4]int) {
arr[0] = 100
fmt.Println("modify的arr",arr)
}
func main() {
var arr = [...]int{1,2,3}
modify(arr)
}
//编译错误,因为不能把[3]int 传递给[4]int
1 ) 切片的英文是slice
2 ) 切片是数组的一个引用,因此切片是引用类型,在进行传递时,遵守引用传递的机制。
3 ) 切片的使用和数组类似,遍历切片、访问切片的元素和求切片长度len(slice)都一样。
4 ) 切片的长度是可以变化的,因此切片是一个可以动态变化数组。
5 ) 切片定义的基本语法:
var 切片名 []类型
//比如:vara[]int
package main
import (
"fmt"
)
func main() {
//演示切片的基本使用
var intArr [5]int = [...]int{1, 22, 33, 66, 99}
//声明/定义一个切片
//slice := intArr[1:3]
//1. slice 就是切片名
//2. intArr[1:3] 表示 slice 引用到intArr这个数组
//3. 引用intArr数组的起始下标为 1 , 最后的下标为3(但是不包含3)
slice := intArr[1:3]
fmt.Println("intArr=", intArr) //[1 22 33 66 99]
fmt.Println("slice 的元素是 =", slice) // 22, 33
fmt.Println("slice 的元素个数 =", len(slice)) // 2
fmt.Println("slice 的容量 =", cap(slice)) //4 切片的容量是可以动态变化
}
我们画图分析一下切片在内存中是如何布局的,这个是一个非常重要的知识点:(以前面的案例来分析)
1 .slice的确是一个引用类型
2 .slice 从底层来说,其实就是一个数据结构(struct结构体)
type slice struct{
ptr *[ 2 ]int
len int
cap int
}
第一种方式:定义一个切片,然后让切片去引用一个已经创建好的数组,比如前面的案例就是这样的。
第二种方式:通过 make 来创建切片.
基本语法:
var 切片名 []type = make([]type,len,[cap])
参数说明:type: 就是数据类型 len: 大小 cap :指定切片容量,可选,如果你分配了 cap, 则要求 cap>=len.
案例演示:
package main
import (
"fmt"
)
func main() {
var slice []float64 = make([]float64, 5, 10)
slice[1] = 10
slice[3] = 20
fmt.Println(slice)
fmt.Println("slice的size=", len(slice))
fmt.Println("slice的cap=", cap(slice))
}
对上面代码的小结:
1 ) 通过make方式创建切片可以指定切片的大小和容量
2 ) 如果没有给切片的各个元素赋值,那么就会使用默认值[int,float=> 0 string=>”” bool=> false]
3 ) 通过make方式创建的切片对应的数组是由make底层维护,对外不可见,即只能通过slice去访问各个元素.
第 3 种方式:定义一个切片,直接就指定具体数组,使用原理类似make的方式
案例演示:
package main
import (
"fmt"
)
func main() {
var strSlice []string = []string{"tom", "jack", "mary"}
fmt.Println("strSlice=", strSlice)
fmt.Println("strSlice的size=", len(strSlice))
fmt.Println("strSlice的cap=", cap(strSlice))
}
方式 1 和方式 2 的区别**(面试)**
方式1是直接引用数组,这个数组是事先存在的,程序员是可见的
方式2是通过make来创建切片,make也会创建一个数组,是由切片在底层进行维护,程序员是看不见的。make创建切片的示意图:
1)从数组引用切片规则左闭合右开,即
切片初始化时 varslice=arr[startIndex:endIndex]
从arr数组下标为startIndex,取到 下标为endIndex的元素(不含arr[endIndex])。
2 ) 切片定义完后,还不能使用,因为本身是一个空的,需要让其引用到一个数组,或者make一 个空间供切片来使用
3 ) 切片可以继续切片
4 ) 用append内置函数,可以对切片进行动态追加
package main
import (
"fmt"
)
func main() {
//使用常规的for循环遍历切片
var arr [5]int = [...]int{10, 20, 30, 40, 50}
//slice := arr[1:4] // 20, 30, 40
slice := arr[1:4]
for i := 0; i < len(slice); i++ {
fmt.Printf("slice[%v]=%v ", i, slice[i])
}
fmt.Println()
//使用for--range 方式遍历切片
for i, v := range slice {
fmt.Printf("i=%v v=%v \n", i, v)
}
slice2 := slice[1:2] // slice [ 20, 30, 40] [30]
slice2[0] = 100 // 因为arr , slice 和slice2 指向的数据空间是同一个,因此slice2[0]=100,其它的都变化
fmt.Println("slice2=", slice2)
fmt.Println("slice=", slice)
fmt.Println("arr=", arr)
fmt.Println()
//用append内置函数,可以对切片进行动态追加
var slice3 []int = []int{100, 200, 300}
//通过append直接给slice3追加具体的元素
slice3 = append(slice3, 400, 500, 600)
fmt.Println("slice3", slice3) //100, 200, 300,400, 500, 600
//通过append将切片slice3追加给slice3
slice3 = append(slice3, slice3...) // 100, 200, 300,400, 500, 600 100, 200, 300,400, 500, 600
fmt.Println("slice3", slice3)
}
切片 append 操作的底层原理分析:
5)切片的拷贝操作
切片使用copy内置函数完成拷贝,举例说明
package main
import (
"fmt"
)
func main() {
//切片的拷贝操作
//切片使用copy内置函数完成拷贝,举例说明
fmt.Println()
var slice4 []int = []int{1, 2, 3, 4, 5}
var slice5 = make([]int, 10)
copy(slice5, slice4)
fmt.Println("slice4=", slice4) // 1, 2, 3, 4, 5
fmt.Println("slice5=", slice5) // 1, 2, 3, 4, 5, 0 , 0 ,0,0,0
}
1 ) string底层是一个byte数组,因此string也可以进行切片处理
3 ) string是不可变的,也就说不能通过 str[ 0 ]=‘z’ 方式来修改字符串
//string是不可变的,也就说不能通过 str[0] = 'z' 方式来修改字符串
str[0] = 'z' [编译不会通过,报错,原因是string是不可变]
4 ) 如果需要修改字符串,可以先将string->[]byte/ 或者 []rune-> 修改 -> 重写转成string
package main
import (
"fmt"
)
func main() {
//如果需要修改字符串,可以先将string -> []byte / 或者 []rune -> 修改 -> 重写转成string
//"hello@atguigu" =>改成 "zello@atguigu"
str := "hello@atguigu"
arr1 := []byte(str)
arr1[0] = 'z'
str = string(arr1)
fmt.Println("str=", str)
}
package main
import (
"fmt"
)
func main() {
//如果需要修改字符串,可以先将string -> []byte / 或者 []rune -> 修改 -> 重写转成string
//"hello@atguigu" =>改成 "zello@atguigu"
str := "hello@atguigu"
// 细节,我们转成[]byte后,可以处理英文和数字,但是不能处理中文
// 原因是 []byte 字节来处理 ,而一个汉字,是3个字节,因此就会出现乱码
// 解决方法是 将 string 转成 []rune 即可, 因为 []rune是按字符处理,兼容汉字
arr1 := []rune(str)
arr1[0] = '北'
str = string(arr1)
fmt.Println("str=", str)
}