一.数组
1.数组的介绍
数组可以可以存放多个同一类型数据。数组也是一种数据类型,在GO中,数组是值类型。
举例:
package main
import "fmt"
func main(){
// 使用数组
// 定义一个数组
var nums [6]float64;
// 给数组的每一个元素赋值
nums[0] = 3.0
nums[1] = 5.0
nums[2] = 1.0
nums[3] = 3.4
nums[4] = 2.0
nums[5] = 50.0
// 遍历数字求平均值
total := 0.0
for i := 0; i < len(nums); i++ {
total += nums[i]
}
avg_num := fmt.Sprintf("%.2f",total / float64(len(nums)))
fmt.Println(avg_num)
}
2.数组定义和内存布局
数组的定义
var 数组 [数组大小]数据类型
如:
var a [10]int;
赋值a[0],a[1]....a[9]
数组的地址可以通过数组名来获取 &arr
数组的首地址就是数组第一个值的地址的。 &arr[0]
数组第二个值的地址就是第一个地址加这个数组类型的字节数,这边如果是int就加8个字节。
package main
import "fmt"
func main() {
var arr [4]int;
fmt.Println("第一个地址:",&arr[0])
fmt.Println("第二个地址:",&arr[1])
fmt.Println("第三个地址:",&arr[2])
fmt.Println("第四个地址:",&arr[3])
// 打印结果:
// 第一个地址: 0xc000052120
// 第二个地址: 0xc000052128
// 第三个地址: 0xc000052130
// 第四个地址: 0xc000052138
}
3.数组的使用
访问数组元素
数组名[下标] 比如:你要使用a数组的第三个元素 a[2]
package main
import "fmt"
func main() {
var intArr [4]int
intArr[0] = 10
intArr[1] = 20
intArr[2] = 30
intArr[3] = 40
fmt.Println("第一个地址:", &intArr[0])
fmt.Println("第二个地址:", &intArr[1])
fmt.Println("第三个地址:", &intArr[2])
fmt.Println("第四个地址:", &intArr[3])
// 打印结果:
// 第一个地址: 0xc000052120
// 第二个地址: 0xc000052128
// 第三个地址: 0xc000052130
// 第四个地址: 0xc000052138
// 遍历数组打印
for i := 0; i < len(intArr); i++ {
fmt.Println(intArr[i])
}
}
4.四种初始化数组的方式
package main
import "fmt"
func main() {
// 四种数组的初始化
// 法一
var numArr01 [3]int = [3]int{1, 2, 3}
fmt.Println("numArr01", numArr01)
// 法二
var numArr02 = [3]int{4, 5, 6}
fmt.Println("numArr02", numArr02)
// 法三
var numArr03 = [...]int{7, 8, 9}
fmt.Println("numArr03", numArr03)
// 法四
var numArr04 = [...]int{1:11, 0:10, 2:12} // 按下表的位置顺序输出,有序
fmt.Println("numArr04", numArr04)
}
5.数组的变量
方式1 :常规变量
package main
import "fmt"
func main() {
// for 遍历数组
var arrDemo = [...]int{1,2,3,4,5}
for i := 0; i < len(arrDemo); i++ {
fmt.Println(arrDemo[i])
}
}
方式二:for-range结构遍历
GO 语言一种独有的结构,可以用来遍历访问数组的元素。
基本语法
for index,value := range arr {
....
}
说明:
(1)第一个返回值index是数组的下标
(2)第二个value是在该下标位置的值
(3)仅在for循环内部可见的局部变量
(4)遍历数组元素的时候,如果不想使用下标index,可以直接把下标index标为下划线_
(5)index和value 的名称不是固定的,可自定义
举例:
package main
import "fmt"
func main() {
var arrDemo = [...]int{1,2,3,4,5}
// for-range遍历方式
for index, value := range arrDemo{
fmt.Println(index,value)
}
}
6.数组的使用注意事项和细节
(1)数组是多个相同类型数据的结合,一个数组一旦声明定义了,其长度是固定的,不能动态变化。
(2)var arr[]int 这时arr就是一个slice切片。
(3)数组中的元素可以是任何数据类型,包括值类型和引用类型,但是不能混用
(4)数组创建后,如果没有赋值,有默认值
数值类型数组:默认值为0
字符串数组: 默认为””
bool数组: 默认值为false
(5)使用数组的步骤1.声明数组并开辟空间 2.给数组各个元素赋值 3. 使用数组
(6)数组的下标是从0开始的
(7)数组的下标必须在指定范围内使用,否则报panic:数组越界,比如:
var arr [5]int 则有效下标为0-4
(8)GO的数组属值类型,在默认情况下是值传递,因此会进行拷贝。数组间不会互相影响。
(9)如想在其中函数中,去修改原来的数组,可以使用引用传递(指针方式)。
(10)长度是数组的一部分,在传递函数参数时,需要考虑数组的长度。
举例:(8),(9)
package main
import "fmt"
func test(arr [3]int) {
arr[0] = 88
}
func demo(arr *[3]int) {
(*arr)[1] = 99
}
func main() {
arr := [3]int{11,22,33}
fmt.Println(arr)
test(arr)
// 数组的值不会因为调用的函数改变而改变
// 拷贝的思想
fmt.Println(arr)
// demo 修改指针的方法可以实现修改数组里面的值
// 传地址
demo(&arr)
fmt.Println(arr)
}
7.数组的应用案例
1.创建一个byte类型的26个元素的数组,分别放置A-Z。使用for循环访问所有元素并打印
出来。提示:字符数据运算 A + 1 > B
package main
import "fmt"
func main(){
// 创建一个byte类型的26个元素的数组,分别放置A-Z。使用for循环访问所有元素并打印
// 出来。提示:字符数据运算 A + 1 > B
// 思路:
//1.声明一个数组 var myChars [26]bytes
// 2.使用for循环,利用 字符可以进行运算的特点来赋值 'A' + 1 -> 'B'
var myChars [26]byte
for i := 0; i < 26; i++ {
myChars[i] = 'A' + byte(i) // 注意需要将 i = > byte
}
for i:=0; i < 26; i++ {
fmt.Printf("%c ",myChars[i])
}
}
2.请求出一个数组最大值,并得到对应的下标
package main
import "fmt"
func main() {
// 请求出一个数组最大值,并得到对应的下标
// 思路 假定第一个元素就是最大值,下标是0
// 然后从第二个元素开始循环比较,如果发现有更大,则交换
var intArr[5] int = [...]int {1,-1,-99,90,11}
maxVal := intArr[0]
maxValindex := 0
for i:=1; i<len(intArr); i++ {
if maxVal < intArr[i] {
maxVal = intArr[i]
maxValindex = i
}
}
fmt.Printf("maxVal=%v maxValindex=%v",maxVal,maxValindex)
}
3.请求一个数组的和和平均值。 for range
package main
import "fmt"
func main(){
// 请求一个数组的和和平均值。 for range
var intArr[5] int = [...]int {1,-1,-5,90,11}
sum := 0
for _,val := range intArr{
// 求和
sum += val
}
fmt.Println(sum,float64(sum)/float64(len(intArr)))
}
4.一个数组的反转
package main
import "fmt"
import "math/rand"
import "time"
func main() {
// 一个数组的反转
// 要求:随机生成5个数,并将其反转打印
// rand.Intn() 生成(0,n)的随机数
// 反转打印,交换的次数是 len / 2
// 第一个与倒数第一 第二与倒数第二
var intArr [5] int
// 为了每次生成的随机数不一样
rand.Seed(time.Now().UnixNano())
for i:=0; i<len(intArr); i++ {
intArr[i] = rand.Intn(100) //
}
fmt.Println(intArr)
temp := 0
for i:=0; i < len(intArr) / 2; i ++{
temp = intArr[len(intArr) - 1 - i]
intArr[len(intArr) -1 - i] = intArr[i]
intArr[i] = temp
}
fmt.Println(intArr)
}
二.切片 Slice
1.切片的基本介绍
(1)slice 就是切片的意思
(2)切片是数组的一个引用,因此切片是引用类型,在进行传递时,遵守引用传递的机制。
(3)切片的使用和数组类似,遍历切片,访问切片的元素和求切片长度len(slice) 都一样。
(4)切片的长度是可以变化的,因此切片是一个可以动态变化的数组
(5)切片定义的基本语法:(特别像python的列表)
var 切片名 [] 类型
举例:
package main
import "fmt"
func main() {
// 先定义一个数组
var intArr [5]int = [...]int{1, 22, 33, 66, 99}
// 定义一个切片
slice := intArr[1:3]
// 表示引用 intArr数组的下标为1的元素 到 下标为3 的元素,结束下标的元素不算在内
fmt.Println(slice) // [22 33]
fmt.Println(len(slice)) // 2
fmt.Println(cap(slice)) // 切片的容量是可以动态变化的,一般是slice的两倍
}
2.切片在内存中形式
(1)slice的确是一个引用类型
(2)slice从底层来说,其实就是一个数据结构struct结构体
(3)slice 里面有三个部分,第一部分是引用数据开始的指针地址,第二部分是存放数据大小,第三部分是存放cap容量。
slice[1] = 44
// 改变切片的值,会使得切片引用的数组相应的数据也改变
fmt.Println(intArr[2]) /
3.切片的使用
(1)第一种方式
定义一个切片,然后让切片去引用一个已经创建好的数组。
如下:
package main
import "fmt"
func main() {
// 先定义一个数组
var intArr [5]int = [...]int{1, 22, 33, 66, 99}
// 定义一个切片
slice := intArr[1:3]
// 表示引用 intArr数组的下标为1的元素 到 下标为3 的元素,结束下标的元素不算在内
fmt.Println(slice) // [22 33]
}
(2)第二种方式
通过make来创建切片
基本语法:
var 切片名 [] type = make([],len,[cap])
type:就是数据类型
len:大小
cap:指定切片容量,可选
package main
import "fmt"
func main() {
var slice []int = make([]int, 4, 10)
fmt.Println(slice)
fmt.Println("slice len=",len(slice),"slice cap=",cap(slice))
slice[0] = 100
slice[2] = 200
fmt.Println(slice)
}
总结:
—1—通过make方式创建切片可以指定切片的大小和容量
—2—如果没有给切片的各个元素赋值,那么就会使用默认值(int,float=>0 string=>””,bool=>false)
—3—通过make方式创建的切片对应的数组是由make底层维护,对外不可见
(3)第三种方式
定义一个切片,直接就指定具体数组,使用原理类似make的方式。
package main
import "fmt"
func main() {
// 第三种方式
var strSlice []string = []string {"tom","jack","mary"}
fmt.Println(strSlice)
}
这种方法没有指定cap的大小,所有与len的大小相同。
4.切片的遍历
切片的遍历和数组一样,也有两种方式。
(1)for 循环常规方式遍历
(2)for-range结构遍历切片
为上面两种方式举例:
package main
import "fmt"
func main() {
// 使用常规的for循环遍历切片
var arr [5]int = [...]int{10, 20, 30, 40, 50}
slice := arr[1:4] // 第二个元素到第4个元素
for i := 0; i < len(slice); i++ {
fmt.Printf("i=%v v=%v\n",i,slice[i])
}
// 使用for--range方式遍历切片
for i, v := range slice {
fmt.Printf("i=%v v=%v\n", i, v)
}
}
5.切片注意事项和细节说明
(1)切片初始化时 var slice = arr[startindex:endindex]
说明:从arr数组下标为startindex开始,取到下标为endindex的元素(不含最后那一个元素)。
(2)切片初始化时,仍然不能越界。范围在[0-len(arr)]之间,但是可以动态增长。
1)var slice = arr[0:end] 可以简写var slice = arr[:end]
2)var slice = arr[strat:len(arr)] 可以简写:var slice = arr[start:]
3) var slice = arr[0:len(arr)] 可以简写:var slice = arr[:]
(3)cap是一个内置函数,用于统计切片的容量,即最大可以存放多少个元素。
(4)切片定义完后,还不能使用,因为本身是一个空的,需要让其引用到一个数组,或者make一个空间供切片来使用。
(5)切片还可以切片。
(6)切片是引用类型,所以在传递时,遵守引用传递机制。要改元素会一起变化。
举例:这个是一个原切片数据和修改后的数据都变化
package main
import "fmt"
func main() {
var slice1 []int
var arr [5]int = [...]int {1,2,3,4,5} // 数组
slice1 = arr[:] // slice1 属于arr数组的一部分,slice2的指针和slice1存的数据一样
var slice2 = slice1
slice2[0] = 10
fmt.Println(slice1)
fmt.Println(slice2)
fmt.Println(arr)
// [10 2 3 4 5]
// [10 2 3 4 5]
// [10 2 3 4 5]
举例2:这个是一个原切片数据不变,修改后的数据变化
package main
import "fmt"
func test(slice []int) {
slice[0] = 100 // 这里修改slice[0]
}
func main(){
//
var slice = []int {1,2,3,4}
fmt.Println("slice=",slice) // [1,2,3,4]
test(slice)
fmt.Println("slice=",slice)
}
6.切片中的使用方法append
用append内置函数,可以对切片进行动态追加。
切片append操作的底层原来分析:
(1)切片append操作的本质就是数组扩容
(2)go底层会创建一个新的数组newArr(安装扩容大小)
(3)将slice原来包含的元素拷贝到新的数组newArr
(4)slice重新引用到newArr(就是整个数组的指针地址变化)
(5)注意newArr是底层来维护的
举例:
package main
import "fmt"
func main(){
// 用append 内置函数,可以对切片进行动态追加
var slice []int = []int {100,200,300}
fmt.Printf("slice=%v\n",slice)
slice = append(slice,400)
fmt.Println("new_slice=",slice)
var demo_slice []int = []int{1,2,3}
// 切片追加切片
slice = append(slice,demo_slice...) // ... 不能忘
fmt.Println("new_new_slice=",slice)
}
7.切片的拷贝操作
切片使用copy内置函数完成拷贝
copy拷贝的数据,数据空间是独立的,之间相互不影响。
如果要拷贝的切片元素个数大于新的切片的元素个数,那么只能够拷贝到新的切片的最大个数的数据到新的切片中。
举例:
package main
import "fmt"
func main(){
var a []int = []int {1,2,3,4,5}
var slice = make([]int, 10) // 10个0的一个切片
fmt.Println(slice)
copy(slice,a)
fmt.Println(slice) // 将a上的元素复制到全为0的切片中
// 切片才能进行拷贝操作
// [0 0 0 0 0 0 0 0 0 0]
// [1 2 3 4 5 0 0 0 0 0]
}
8.string 与slice
(1)string底层是一个byte数组,因此string也可以进行切片处理
(2)string是不可变的,也就是说不能通过str[0] = ‘z’ 方式来修改字符串的内容
(3)如果需要修改字符串,可以先将string-->[]byte再重新转回来 或者 []rune -->修改-->重写转成string (其实就相当于形成一个新的字符串)
举例:修改字符串举例
package main
import "fmt"
func main() {
//string底层是一个byte数组 ,因此string也可以进行切片
str := "hello_hello_hsz"
// 使用切片获取hsz
slice := str[12:]
fmt.Println("slice=", slice)
// string --> byte 转成byte数组
arr := []byte(str)
arr[0] = 'z'
str = string(arr)
fmt.Println("str=", str)
// 可以处理英文和数字,但是不能处理中文
// []byte 字节来处理,而一个汉字,是3个字节,因此就会出现乱码
// 解决方法: string 转成 []rune 因此rune是按字符来计算处理的,兼容汉字
arr1 := []rune(str)
arr1[0] = '好'
str = string(arr1)
fmt.Println("str=", str)
}
9.斐波那契数列放到切片中
package main
import "fmt"
func fbn(n int) []uint64 {
// 声明一个切片,切片大小n
fbnSlice := make([]uint64, n)
// 第一个数和第二个数的斐波那契为1
if n == 1 {
fbnSlice[0] = 1
} else if n == 2 {
fbnSlice[0] = 1
fbnSlice[1] = 1
} else {
fbnSlice[0] = 1
fbnSlice[1] = 1
// 进行for循环来存放斐波那契的数列
for i := 2; i < n; i++ {
fbnSlice[i] = fbnSlice[i-1] + fbnSlice[i-2]
}
}
return fbnSlice
}
func main() {
// 编写一个函数 fbn(n int )
// 1. 可以接收一个 n int
// 2.能够将斐波那契的数列放到切片中
// 提示: slice[0] = 1 slice[1] = 1 slice[2] = 2 slice[3] = 3 slice[4] = 5
fnbSlice := fbn(4)
fmt.Println("fbnSlice=", fnbSlice)
}
|
请发表评论