内容详细
1 切片
// 数组---》连续存储同种元素的数据结构---》数组一旦定义,大小是不能改变
// 存在的问题,不知道要存多少数据,需要一种数据结构像python列表一样,可以只管放值,不用管大小
// js:数组
// python:列表
// java: ArryList
// go:切片
// 定义
切片是由【数组】建立的一种方便、灵活且功能强大的包装。切片本身【不拥有任何数据】。它们只是【对现有数组的引用】---》切片底层依赖于数组,是对数组的引用
延伸:底层数组如果发生变化,切片也变了;切片发生变化,底层数组也变化
package main
import "fmt"
// 切片
func main() {
// 1 切片的定义--->使用数组定义出来---》切片类型--》[]类型
// 值类型的零值--->区分于引用类型---》零值分别是
// 数字:0
// 字符串:""
// 布尔: false
//var a [10]int // 数组是值类型,不初始化,有默认的零值--->默认的零值是数组内类型的零值
// 基于数组--》生成切片
//var s =a[:] // 把数组从第0个位置切到最后一个位置,赋值给s切片
//var s []int=a[:] // 中括号中只要不放任何东西就是切片,放了数字就是数组,对新手很容易混
//var s []int=a[2:4]
//fmt.Println(s)
// 2 切片定义并初始化---》通过make初始化--->切片只定义不初始化,默认0值为:nil
// 引用类型的零值是nil(类比python的None),值类型的零值各不相同
//var s []int=make([]int,3,4) // 定义一个长度为3的 []int 切片,容量是4
//fmt.Println(s) // [0 0 0]
// 3 切片直接定义并初始化---》类比数组初始化
//var s []int=[]int{3,4,5}
//fmt.Println(s)
// 4 切片的底层实现
//var a [10]int=[10]int{1,2,3,4,5,6,7,8,9,10}
//var b []int=a[:]
//fmt.Println("数组a:",a)
//fmt.Println("切片b:",b)
// 4.1 切片和数组的修改都会相互影响
//a[0]=999
//fmt.Println("数组a:",a)
//fmt.Println("切片b:",b) // 也改了,影响了
//b[1]=888
//fmt.Println("数组a:",a)// 也改了,影响了
//fmt.Println("切片b:",b)
// 4.2 切片的长度和容量--》切片的长度指的是目前切片多大,容量指的是,切片总共能存多少
// len 内置函数查看长度 cap内置函数查看容量 ---》只针对于切片类型
// 数组类型 a
//fmt.Println(len(a)) // 数组只有个长度
//fmt.Println(cap(a)) // 数组没有容量这一说,但是用的时候不报错(没有人这么用)
// 切片类型b
//fmt.Println(len(b)) //b切片的长度是10
//fmt.Println(cap(b)) // b切片的容量是:10
//var a [10]int=[10]int{1,2,3,4,5,6,7,8,9,10}
//var b []int=a[2:4]
////a[0]=999
//b[0]=999
//fmt.Println(a)
//fmt.Println(b)
//fmt.Println(len(b)) //b切片的长度是2
//fmt.Println(cap(b)) // b切片的容量是:8 指向起始位置开始到末尾
// 演示
//var a [10]int=[10]int{1,2,3,4,5,6,7,8,9,10}
//var b []int=a[7:8] // 第一个数组和最后一个数字都可以不写
//fmt.Println(len(b)) //b切片的长度是1
//fmt.Println(cap(b)) // b切片的容量是:3 指向起始位置开始到末尾
// 切片底层基于数组,指向数组的某个位置---》切片的容量是从指向位置开始导数组结尾的个数
// 5 切片追加元素--->数组不允许追加元素,切片可以
//var a [10]int=[10]int{1,2,3,4,5,6,7,8,9,10}
//var b []int=a[7:8]
//b[0]=999
//fmt.Println(a)
//fmt.Println(b)
//// 切片追加元素--->内置函数 python列表 列表对象.append()
//b=append(b,888)
//fmt.Println("切片b:",b)
//fmt.Println("数组a:",a)
//fmt.Println(len(b)) // 2
//fmt.Println(cap(b)) //3
//// 现在长度是2,容量是3,再追加 777
//b=append(b,777)
//fmt.Println("切片b:",b)
//fmt.Println("数组a:",a)
//fmt.Println(len(b)) // 3
//fmt.Println(cap(b)) //3
//// 现在长度是3,容量是3,再追加 666---》超过了底层数组大小---》
////1 重写申请一个底层数组,把切片的值copy过去 2 切片的容量变为:原来切片容量的两倍 3 现在这个切片跟原来数组就没关系了
//b=append(b,666)
//fmt.Println("切片b:",b) //[999 888 777 666]
//fmt.Println("数组a:",a) //[1 2 3 4 5 6 7 999 888 777]
//fmt.Println(len(b)) // 4
//fmt.Println(cap(b)) //6
//// 修改切片,不会影响最原来的数组了
//b[0]=9
//a[9]=7
//fmt.Println("切片b:",b) //[9 888 777 666]
//fmt.Println("数组a:",a) //[1 2 3 4 5 6 7 999 888 7]
//6 make创建切片--->底层数组取不到
//var s =make([]int,3,4)
//fmt.Println(s) // [0 0 0 ]
//fmt.Println(len(s)) // 3
//fmt.Println(cap(s)) //4
//var s =[]int{3,4,5}
//fmt.Println(s) // [3,4,5]
//fmt.Println(len(s)) // 3
//fmt.Println(cap(s)) //3
// 7 切片的零值 --->nil
//var s []int=make([]int,2,2) // 定义,有初始化
////var s []int // 只定义,没有初始化
//fmt.Println(s[0]) // 不能取值赋值,因为没有初始化
//fmt.Println(s) // [ ]
//if s==nil {
// fmt.Println("我是nil")
//}else {
// fmt.Println("我不是nil")
//}
// 8 切片的参数传递---》切片是引用类型--》当参数传递在函数中修改---》会影响原来的
//var s []int=[]int{3,4,5}
//testS(s)
//fmt.Println("调用之后:",s) //[999 4 5]
// 为什么?go语言的参数传递都是copy传递---》因为切片是个引用(地址,指针)--》把切片复制了一份传入了
// 由于切片是引用,在函数中根据引用改了值,改了原来的底层数组,大家都会受影响
// 8.1 演示二
//var s []int=make([]int,3,4)
//testS(s)
//fmt.Println("调用之后:",s) //[999 0 0]
// 9 copy 切片
//var a [100000]int
//var b =a[:3]
//fmt.Println(b) //[0 0 0]
//b[0]=999
//b[2]=222
//fmt.Println(b) //[999 0 0] 使用b,虽然只用3个值,但是底层数组很大,内存占用大
//// 把b这个切片,copy另一个新切片上
//var c= make([]int,2,2) // 基于的底层数组,数组大小是3
//copy(c,b)
//fmt.Println(c) // [999 0]
//// copy两个切片,上面的案例是两个切片长度一样,如果不一样呢? 拉链
//10 多维切片-->每一层都要初始化
//var s [][]int=make([][]int,2,2)
//var s [][]int
//fmt.Println(s[0])
//fmt.Println(s[0][0]) // 报错,第二层没有初始化
var s [][]int=[][]int{{2,3},{4,4,4,5},{6,7,8}}
fmt.Println(s)
fmt.Println(s[0][1])
// 循环切片 两层for循环,跟数组一样
}
func testS(s []int) { // 在函数中追加切片,一定要注意有没有超过容量,如果超过了容量,超过后再改的值,就不会影响原来的了
fmt.Println(s) //[0 0 0]
s[0]=999 //[999 0 0] 会影响原来的
s=append(s,888,777) //[999 0 0 888 777],追加了超过了底层数组,不依赖于原来的数组了
s[1]=666 //[999 666 0 888 777] // 不影响原来的数组了
fmt.Println(s) // [999 666 0 888 777]
}
2 python列表底层实现
// python 的列表是用c语言实现的
// c语言的结构体实现的,底层就是数组
{
变量1---》列表的长度
变量2 ---》指向了底层数组---》涉及到扩容,这个就变了
变量3--->底层数组大小
}
[0x111,0x222,0x333]
0x111:1
0x222:'lqz'
0x333:[1,2,3]
// 插入刘亦菲
[0x111,0x444,0x222,0x333] --->底层数组扩容--》列表插入操作,时间和空间复杂度都是o(n)
0x111:1
0x444:"刘亦菲"
0x222:'lqz'
0x333:[1,2,3]
// 移除刘亦菲
[0x111,0x222,0x333,None] --->移除,右侧位置全都往左移,时间和空间复杂度都是o(n)
// 最后一个位置插入值,不需要重新构造新数组,直接在原来基础上插入即可
// 插入两个值,超过了底层数组大小---》扩容
3 map
//1 什么是 map
key-value:python中的字典,但是在go中,在定义阶段,key和value类型固定了,后期不能改
js:对象
python:字典---》最好用的
go:map
java:HashMap
// 2 如何创建 map ?
给 map 添加元素
获取 map 中的元素
删除 map 中的元素
获取 map 的长度
Map 是引用类型
Map 的相等性
4 hash类型的底层实现
// hash冲突解决
1 开放寻址法(open addressing)
开放寻址法中,所有的元素都存放在散列表里,当产生哈希冲突时,通过一个探测函数计算出下一个候选位置,如果下一个获选位置还
是有冲突,那么不断通过探测函数往下找,直到找个一个空槽来存放待插入元素。
2 再哈希法
方法是按顺序规定多个哈希函数,每次查询的时候按顺序调用哈希函数,调用到第一个为空的时候返回不存在,调用到此键的时候返回
其值。
3 链地址法(普遍常用:java hashmap---》红黑树实现原理)
将所有关键字哈希值相同的记录都存在同一线性链表中,这样不需要占用其他的哈希地址,相同的哈希值在一条链表上,按顺序遍历就
可以找到
4 公共溢出区
其基本思想是:所有关键字和基本表中关键字为相同哈希值的记录,不管他们由哈希函数得到的哈希地址是什么,一旦发生冲突,都填
入溢出表。
https://blog.csdn.net/General_zy/article/details/122038164
5 字符串
package main
import "fmt"
// 字符串--》go语言中存储---》utf8存储
// python:2.x版本 3.x版本字符串的编码unicode
func main() {
// 1 字符串长度,len指的是字节
//var s ="hello world"
//fmt.Println(len(s)) // 11个字符,11 字节
//var s ="hello world 中国"
//fmt.Println(len(s)) //14个字符,18 字节 ,utf-8编码一个中文字符,占3个字节
// python中的len指的是字符数
// 字符长度
//fmt.Println(utf8.RuneCountInString(s)) // run 是字符 14
//2 for循环字符串
//var s ="hello world"
//var s ="hello world 中国"
//for _,v:=range s{ // range循环的就是字符
// fmt.Println(string(v)) // rune类型
//}
//for i:=0;i<len(s);i++{ // 按字节循环,转成字符串会出乱码
// fmt.Println(string(s[i]))
//}
//for i:=0;i<utf8.RuneCountInString(s);i++{ // 按字节循环,转成字符串会出乱码
// fmt.Println(string(s[i])) // 按字节取,取出来转还是有问题,还是要用range循环
//}
// 3 字符串是不可变的
//var s ="hello world 中国"
////s[0]='h'
//fmt.Println(s[0]) // s是unicode编码的104
// 4 通过字节切片
//var b []byte=[]byte{104,105,106,'a'}
//fmt.Println(string(b)) //hija
// 5 根据字符切片构造
//var b []rune=[]rune{104,105,10688,'中'}
//fmt.Println(string(b)) //hij中
// 6 字符串和字节切片,和字符切片相互转换
// 根据字符取值---》没有操作---》转成字符切片,再取
//var s ="hello world 中国"
//var b []rune=[]rune(s)
//fmt.Println(b[12]) //20013
//fmt.Println(string(b[13]))
var s ="hello world 中国"
var b []byte=[]byte(s)
fmt.Println(b[10]) //100
fmt.Println(string(b[10]))
}