go指针
1、类型指针:不能进行偏移和运算。无需copy数据
2、切片:构成: 指向起始元素的原始指针,元素数量、容量组成【元素数量和容量的区别:】
容器:
尽管命名变量在一定程度上可以满足函数要求,但是复杂的算法、结构、逻辑就需要使用容
1、数组:固定长度、连续内存 【go语言中可以修改数组成员,但是数组大小不可变】
2、go的数组和切片都是沿用c语言
var 数组名 [元素数量] T //T表示数组元素类型为任意基本类型(包括数组本身)
eg:
1、var team [3]string
team[0] = "hammer" //第一种声明方式
2、var team = [1]string{"hammer"} //这种方式需要后面元素数量和前面声明的一致
3、for k,v in:=range team{} //数组也可以进行键值的索引
3、关于切片:长度动态分配、连续内存
struct: (地址[本质上地址指向数组的首地址]。大小[表示数组的现有长度],容量 )
slice [start : end] //指向连续区域,可以是数组或者切片本身
wrong:
1、根据索引位置获取切片元素是,索引必须(0~len-1)否则会报错,生成切片时,
if end = len(slice) 则不会报错
2、切片 islike 指针,但是,指针可以运算却会越界,切片在指针的基础上增加了大小,因此更安全
3、切片重置:[start :end] = [0:0]
4、声明切片 ===============?question==============
var name []T //像数组创建,只是没有声明长度
//声明字符串切片
var str List []string
//声明整形切片
var num List []int
//声明空切片
var num List Empaty = []int{}
5、make函数构造切片
make ([]T, size, cap) //cap提前分配空间,降低了多次分配空间造成的性能问题,len函 //数对应的是size
6、构造和引用切片的区别
make()函数发生内存分配动作,切片引用只是将新的切片结构指向已经分配好的区域,就像一个框框住了已有的某些东西,并没有发生内存分配操作;
append()函数,在使用make()函数后、生成引用切片后、声明切片后,均可以使用
7、切片添加元素
每个切片指向一片内存空间,当内存空间无法继续容纳,就进行扩容操作【2^n扩容规律】,在当前不扩容的环境中,指针的首地址不会变化,所谓的扩容,其实在新的内存空间建立了新的切片 //类 //似于公司搬家,只有盛不下再搬,公司名(变量名是不会变 的)
car = make([]T, size, cap)
car = append(car, "元素值","元素值") //可以添加一个元素、多个元素、一个切片等
//为什么不能使用car.append("bmw") ,这是因为go中没有引用类型,因为go不存在引用赋值,所以append的本质是在底层创建了新的数组。
8、切片删除元素
没有特定函数,利用append()函数将删除点前后连接即可
连续容器的元素删除:必须将删除元素后的元素移动到新的位置,很耗费精力。双链表则是很好的办法,但是空间不连续
4、关于map映射: 建立事物关联的容器 【用于使用任意类型之间的关联关系:比如学号与学 生,名字与档案】
go语言的映射关系容器是map,map使用散列表(hash)实现,数组的每一个元素是一个列表
def:
map[key type] value type //map映射的 v默认值为0,键可以使用函数意外的任意类型
对于默认值0引发的歧义:
v, ok := car['bmw'] //用于判断bmw键是否存在,万一有键的值就是0,就误会了
1、map映射的创建:
car := make(map[string]int) //创建map映射 如果不创建使用会触发宕机错误
//声明时就填充内容,
car := map[string]int{”字典形式写出内容“} //有点类似json格式
2、go语言中的遍历格式都差不多
for range来完成这个内容
@ for _, v := range car{} //只遍历值
@ for k := range car{} //遍历输出跟输入的顺序没有关系,除非另行,否则不能得到某种期望顺序
3、删除键值对
delete[car,"bmw"]
4、map元素清空:只能创建一个新的map,垃圾回收效率很高的
5、关于channel --------在多个goroutine间通信的管道
函数的单纯的并发执行来提高运行效率是没有用的,必须首先函数值之间交换数据才有意义。
@【共享内存交换数据:在不同的goroutine中容易发生竞态问题,保证正确性则需要使用互斥量对内存加锁,但会造成性能问题】go语言使用通道通信的办法代替共享内存【这是不同协程之间通信的方式】
@和map类似,也是使用make创建的底层数据结构的引用
****当我们复制一个channel或用于函数参数传递时,我们只是copy了一个channel引用,这里调用者和被调用者引用了同一个channel对象
1、创建channel
make(chan Type) 等价于: make(chan Type,0) //使用make函数来创建:
make(chan Type, capacity) //通道类型:int,string、interface{}
@about capacity
capacity = 0: 代表无缓冲阻塞读写
capacity > 0: 代表有缓冲,无阻塞,直到写满capacity个元素才写入
2、接收和发送数据
channel通过操作符<-来接收和发送数据,发送和接收数据语法:
channel <- value //发送value到channel
<-channel //接收并将其丢弃
x := <-channel //从channel中接收数据,并赋值给x
x, ok := <-channel //x, ok = <- c //从Channel接收一个值,如果channel关闭 //了或没有数据,那么ok将被置为false, 功能同上,同时检 //查通道是否已关闭或者是否为空、
*****默认情况下,channel接收和发送数据是阻塞的,除非另一端已经准备好,这样就使得 goroutine同步变的更加的简单,而不需要显式的lock。
//可选的<-代表channel的方向。如果没有指定方向,那么Channel就是双向的,既可以接 收数据,也可以发送数据,
=====
chan T // 可以接收和发送类型为 T 的数据
chan <- float64 // 发送:只可以用来发送 float64 类型的数据
<-chan int // 接收:只可以用来接收 int 类型的数据
@什么情况下发生通道阻塞呢?
channel 一定要初始化后才能进行读写操作,否则会永久阻塞
¥无缓存
从无缓存的 channel 中读取消息会阻塞,直到有 goroutine 向该 channel 中发送消息;同理,向无缓存的 channel 中发送消息也会阻塞,直到有 goroutine 从 channel 中读取消息
¥有缓存
写:接收端没有准备好接收数据;channel close;buf pool写满会阻塞写入[此时会等待goroutine从channel中读取元素,腾出空间]
读:接收端准备好了,发送端没有发送数据;buf pool中不为空【一般情况下会在buf pool满的时候才会读取】;
@channel的使用过程
1、先加lock
2、如果chann close,则直接panic(宕机)
3、如果有G在等待数据,就将数据直接发送
// //
1、如果buf还有剩余,就将数据保存在buf
2、sendx++,同时指向下一个发送对象,
3、sendx == 0, 则表示buf满了
@channel的CSP模型:
1、发送和接收双方的身份是未知的
2、为了进程的不阻塞,程序员需要检查传入消息,他们是没有顺序的
3、接收消息和发送消息必须是同步的,也就是必须在接收方准备好才能发送消息(类似乎握手机 制)
@可以通过range,像操作slice或者map一样操作缓存类型的channel
应该在生产者的地方关闭channel,而不是消费的地方去关闭它,这样容易引起panic。
channel不像文件之类的,不需要经常去关闭,只有当你确实没有任何发送数据了,或者你想显式的
结束range循环之类的===============当出现goroutine阻塞的情况时,我们可以利用select来设
置超时,避免整个程序进入阻塞的情况===========
@即只可写入或只可读的channel,事实上 channel 只读或只写都没有意义,所谓的单向 channel 其实知识声明时用
@new()方法和make()方法有什么区别呢?
返回值
从定义中可以看出,new返回的是指向Type的指针。 make直接返回的是Type类型值。
入参
new只有一个Type参数,Type可以是任意类型数据。 make可以有多个参数,其中第一个参数与new的参数相同,但是只能是slice,map,或者chan中的一种。对于不同类型,size参数说明如下:
- 对于slice,第一个size表示长度,第二个size表示容量,且容量不能小于长度。如果省略第二个size,默认容量等于长度。
- 对于map,会根据size大小分配资源,以足够存储size个元素。如果省略size,会默认分配一个小的起始size。
- 对于chan,size表示缓冲区容量。如果省略size,channel为无缓冲channe
,或者chan中的一种。对于不同类型,size参数说明如下:
- 对于slice,第一个size表示长度,第二个size表示容量,且容量不能小于长度。如果省略第二个size,默认容量等于长度。
- 对于map,会根据size大小分配资源,以足够存储size个元素。如果省略size,会默认分配一个小的起始size。
- 对于chan,size表示缓冲区容量。如果省略size,channel为无缓冲channe