go容器

本文深入解析了Go语言中的指针类型、切片概念、数组与切片的区别,以及map和channel在并发编程中的应用。重点讲解了内存分配、扩容机制、通道通信原理和CSP模型,适合理解高级Go语言特性的开发者。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值