golang学习笔记第二部分:9.数组和切片

golang学习笔记第二部分:9.数组和切片

18、数组

1)数组定义:存放多个同一类型的数据,go语言中,数组是值类型 var 数组名[数组大小]数据类型
var a [5]int var b [3]int = [3]int{1,2,3} var c = [3]int{1,2,3} var d = […]int {1,2,3} //…代表让系统去判断数据大小 2)数组的遍历
方式一:常规遍历for 方式二:for-range结构遍历 for index,value := range array01{ … }
index是数组的下标、value是下标对应的值,这两个变量都是仅在for循环内的局部变量,不需要下标时,可以用_替换index,index和value名称是可以自定义的 3)注意事项和细节

  • 数组内的数据类型相同,不能混合使用,可以是值类型或引用类型
  • 数组的长度是固定的,不能动态变化
  • var arr []int 这个其实是切片
  • 数组创建后,如果没有赋值,有类型的默认值
  • go的数组是值类型,默认情况下是值传递,因此会进行值拷贝,数组间不会相互影响
  • 如果想在其他函数中,去修改原来的数组,可以使用引用传递(指针方式),在数组数据量非常大的情况下,值拷贝的效率远低于引用传递,因为只复制一个内存地址
  • 长度是数组类型的一个部分,在传递函数参数时,需要考虑数组的长度

数组基础

package main

import "fmt"

func test01(arr [3]int) { //[3]int [4]int 在这里不是一种数据类型,因为长度不一样
	arr[0] = 88
	fmt.Println("in func test01", &arr[0], arr)
}

func test02(arr *[3]int) {
	(*arr)[0] = 99 //指向数组的指针的使用
	fmt.Printf("arr指针的地址 %p\n", &arr)
	fmt.Println("in func test02", &arr[0], *arr)
}

func main() {
	// var score [6]float64
	// fmt.Printf("数组的地址%p\n", &score)
	// fmt.Printf("数组首个元素的地址%p\n", &score[0])
	// for i := 0; i < len(score); i++ {
	// 	fmt.Printf("请输入第%d个元素", i+1)
	// 	fmt.Scanln(&score[i])
	// }

	// for i := 0; i < len(score); i++ {
	// 	fmt.Printf("score[%d]=%v\n", i, score[i])
	// }

	// var b [3]int = [3]int{1, 2, 3}
	// var c = [3]int{1, 2, 3}
	// var d = [...]int{1, 2, 3}             //...代表让系统去判断数据大小,固定写法
	// e := [...]int{1: 123, 2: 111, 0: 333} //指定下标赋值,类型推导
	// fmt.Println("b", b)
	// fmt.Println("c", c)
	// fmt.Println("d", d)
	// fmt.Println("e", e)

	// for i, v := range e {
	// 	fmt.Println(i, v)
	// }

	arr := [3]int{11, 22, 33} //如果这里定义[4]int,参数传入会报错
	test01(arr)
	fmt.Println("in func main", &arr[0], arr) //因为是值类型,因此arr[0] = 11,而不是88,可以看到2个arr[0]地址也不一样,函数test和main都单独对arr数组生成了独立的内存空间

	test02(&arr)
	fmt.Println("in func main", &arr[0], arr) //可以看到,指针引用后,函数test和main里的arr数组值和内存地址是一样的
}

数组练习

package main

import (
	"fmt"
	"math/rand"
	"time"
)

func exe01() {
	//创建一个byte类型的数组,放置A-Z,使用for访问所有元素及打印
	var myChars [26]byte
	for i := 0; i < 26; i++ {
		myChars[i] = 'A' + byte(i)
	}

	for i := 0; i < 26; i++ {
		fmt.Printf("%c ", myChars[i])
	}
}

func exe02() {
	//请求出一个数组的最大值,并得到对应的下标
	//思路
	//1、声明一个数组,填入内容
	//2、假定第一个元素就是最大值,下标是0
	//3、用假定的第一个元素和后面每个元素循环比较,有更大的数则交换
	intArr := [...]int{1, -1, 33, 5, 15, 111}
	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 \n", maxVal, maxValIndex)
}

func exe03() {
	//请求出一个数组的和与平均值,使用for-range
	intArr2 := [...]int{1, -1, 33, 5, 15}
	sum := 0
	for _, val := range intArr2 {
		sum += val
	}
	fmt.Printf("数组的和=%v,平均值=%v \n", sum, float32(sum)/float32(len(intArr2))) //go运算,int除法结果是整数,需要转换浮点类型
}

func exe04() {
	//随机生成5个数,并将其反转打印
	//思路
	//1、使用rand.Intn()函数生成随机数,放入数组
	//2、反转打印:交换次数=数组大小/2,第一个和最后一个交换,第二个和倒数第二个交换,依次类推
	var intArr3 [33]int
	arrlen := len(intArr3)
	rand.Seed(time.Now().UnixNano()) //用纳秒时间戳生成随机种子
	for i := 0; i < arrlen; i++ {
		intArr3[i] = rand.Intn(100)
	}
	fmt.Printf("交换前数组 %v\n", intArr3)
	for i := 0; i < arrlen/2; i++ {
		tmp := intArr3[i]                //第i个放临时变量
		intArr3[i] = intArr3[arrlen-i-1] //倒数i个赋值给第i个,数组下标从0开始,因此要-1,5个元素的数组,最后一个下标是4
		intArr3[arrlen-i-1] = tmp        //临时变量(第i个)赋值给倒数i个
	}
	fmt.Printf("交换后数组 %v\n", intArr3)
}

func main() {
	// exe01()
	// exe02()
	// exe03()
	exe04()
}

19、切片

1)切片是数组的一个引用,因此切片是引用类型;切片的长度是可以变化的;切片的使用和数组类似 2)切片定义的基本语法: var 切片名 []类型: var a []int 3)切片的使用: 方式一、定义一个切片,引用一个已经创建好的数组;
方式二、通过make创建切片:var slice []int = make([]type,len,[cap]) //cap可选 方式三、定义一个切片,直接指定具体数组,原理和make类似 4)切片遍历的方法和数组相同:
常规for、for-range 5)引用数组的简写法:x到结束 arr[x:end]、开始到x arr[start:x]、全部 arr[:]
6)切片可以继续切片 7)append内置函数,可以对切片进行动态追加,容量不够时会动态扩容及生成一个新的底层数组 8)切片可以追加切片,固定写法 slice = append(slice,slice…),不能追加数组 9)
切片的拷贝操作,使用copy内置函数操作,拷贝后仍然是切片类型 copy(b,a)
a,b都是切片类型,复制a每个元素到b对应的位置,b其他的空间不变,改变的是b切片;只是复制值,相对的空间还是独立的;a长度大于b时,从前往后复制能容纳的元素 10)string和slice
string底层是一个byte数组,因此可以进行切片 string是不可变的,不能通过str[0]='a’来修改;可以通过将string -> []byte或[]rune后再修改转换后的切片

切片基础

package main

import (
	"fmt"
)

func demo1() {
	var intArr [5]int = [...]int{1, 2, 3, 4, 5}
	slice := intArr[1:3]
	fmt.Println("切片的值,长度,容量是:\n", slice, len(slice), cap(slice))
	fmt.Printf("%p %p \n", &intArr[1], &slice[0]) //数组和切片的地址是相同的
	//切片本质是一个struct: 内存地址 len cap

	slice[0] = 22 //修改切片,实际是修改了引用的数组
	fmt.Println(intArr, slice)
	slice = append(slice, 11)
	fmt.Println("切片的值,长度,容量是:\n", slice, len(slice), cap(slice))
	slice = append(slice, 22)
	fmt.Printf("%p %p \n", &intArr[1], &slice[0])
	fmt.Println("切片的值,长度,容量是:\n", slice, len(slice), cap(slice))
	slice = append(slice, 33)
	fmt.Println("切片的值,长度,容量是:\n", slice, len(slice), cap(slice))
	//容量是动态调整的
	fmt.Printf("%p %p \n", &intArr[1], &slice[0])
	//动态扩容后,生成了一个新的底层数组,内存地址就不一样了
	slice[0] = 222 //这时候再修改,原来的数组不会变了
	fmt.Println(intArr, slice)
}

func demo2() {
	//切片的另外2种创建方式,底层数组不可见,通过slice操作
	var slice1 []int = []int{1, 2, 3}
	var slice2 []int = make([]int, 2, 6)
	fmt.Println(slice1, slice2)
	//遍历方法和数组一样
	for i := 0; i < len(slice1); i++ {
		fmt.Println(slice1[i])
	}

	for i, v := range slice1 {
		fmt.Println(i, v)
	}
}

func demo3() {
	var a []int = []int{1, 2, 3, 4, 5}
	var b = make([]int, 10)
	copy(b, a)
	fmt.Println(a, b)
}

func demo4() {
	str := "you are so beatuful"
	slice := str[4:7]
	fmt.Println(slice)
	// str[1] = '1' //编译报错
	slice2 := []byte(str)
	slice2[0] = 'x' //这样修改不报错
	str = string(slice2)
	fmt.Println(str) //byte类型不能处理中文,因为一个中文3个字节,rune按字符处理,兼容汉字
}

func main() {
	demo4()
}

切片练习

package main

import "fmt"

//编写一个函数,将斐波那契数列放入切片
func finb(n int) []uint64 {
	//思路
	//斐波那契数列:1,2,3,5,8
	//定义一个函数,返回切片
	slice := make([]uint64, n)
	slice[0] = 1
	slice[1] = 1
	for i := 2; i < n; i++ {
		slice[i] = slice[i-1] + slice[i-2]
	}
	return slice
}

func main() {
	fnb := finb(10)
	fmt.Println(fnb)
}

你可能感兴趣的:(golang学习笔记,golang,数据结构,开发语言)