我发现切片映射函数和通道经常作为引用类型一起提到。然而,我注意到有些东西表现出非引用行为,就像它们会过时一样:
var s []int
//must update slice value
s = append(s, ...)
或
//must use pointer if we want to expose the change
func foo(s *[]int) error
//or change the function signature to return it like _append_
func foo(s []int) (r slice, err error)
通常我是通过记住slice描述器实现的内部组件来理解这一点的:slice值可以被看作len、cap和数据指针的结构。
但是地图值永远不需要像
m := make(map[string]int)
...
// don't know how to express with insertion, but you know what i mean.
m = delete(m, "well")
为什么?映射值只是映射描述符的指针吗?如果是这样,为什么不这样做呢?
Slice是对连续内存块的薄如纸的包装,部分或全部重用该内容通常是有益的(避免复制数据)。地图没有这些特征。这是一个具有复杂行为的复杂数据结构,您不能重用它的存储(就像您对切片所做的那样)。
在Go中没有像C中那样的引用类型。在围棋中,一切都是按价值传递的。在Go中使用术语“reference type”时,它表示引用它们应该表示的数据的类型(通过指针)。
切片是由类型反映的小的、类似结构的数据结构。SliceHeader
:
type SliceHeader struct {
Data uintptr
Len int
Cap int
}
它包含一个指针,指向底层数组中切片的第一个元素(SliceHeader.Data
field)。该结构很小,作为值传递很有效,无需传递其地址(并取消引用以间接访问其任何字段)。切片的元素不存储在切片标头中,而是存储在标头内存区域之外的数组中。“修改”原始元素的“将”表示修改原始元素的“将”。
当您将(超过0)个元素附加到切片时,标头中的Len
字段必须更改,因此使用附加元素描述切片的新切片必须与附加之前的切片不同,这就是为什么您需要指定内置append()
函数的返回值。(其他值也可能会更改,但Len
sure必须更改。)
映射被实现为指向运行时的指针。hmap
结构:
type hmap struct {
// Note: the format of the hmap is also encoded in cmd/compile/internal/gc/reflect.go.
// Make sure this stays in sync with the compiler's definition.
count int // # live cells == size of map. Must be first (used by len() builtin)
flags uint8
B uint8 // log_2 of # of buckets (can hold up to loadFactor * 2^B items)
noverflow uint16 // approximate number of overflow buckets; see incrnoverflow for details
hash0 uint32 // hash seed
buckets unsafe.Pointer // array of 2^B Buckets. may be nil if count==0.
oldbuckets unsafe.Pointer // previous bucket array of half the size, non-nil only when growing
nevacuate uintptr // progress counter for evacuation (buckets less than this have been evacuated)
extra *mapextra // optional fields
}
正如您所看到的,这是一个比切片头复杂得多的数据结构,而且要大得多,将其作为值传递是没有效率的。
从映射中添加/删除元素(键值对)存储在由该结构的字段引用的桶中,但是由于映射在引擎盖下作为指针处理,因此您不需要分配此类操作的结果。
为了完成,通道也被实现为指针,指向runtime
包的hchan
类型:
type hchan struct {
qcount uint // total data in the queue
dataqsiz uint // size of the circular queue
buf unsafe.Pointer // points to an array of dataqsiz elements
elemsize uint16
closed uint32
elemtype *_type // element type
sendx uint // send index
recvx uint // receive index
recvq waitq // list of recv waiters
sendq waitq // list of send waiters
// lock protects all fields in hchan, as well as several
// fields in sudogs blocked on this channel.
//
// Do not change another G's status while holding this lock
// (in particular, do not ready a G), as this can deadlock
// with stack shrinking.
lock mutex
}
这也是一个“fat”结构,处理方式类似于映射值。
见相关问题:
要在参数中使用的切片与贴图
使用值接收器追加到具有足够容量的片
golang切片是否按值传递?
Go中的“值语义”和“指针语义”是什么意思?
问题内容: 我发现切片图功能和通道经常一起作为 参考类型 提及。但是我注意到,切片的东西不会表现出参考行为,就像它们会过时一样: 要么 通常,我会通过牢记切片描述符实现的内部组件来理解这一点:切片值可以视为len,cap和data指针的结构。 但是地图值永远不需要像 为什么?映射值仅仅是指向映射描述符的指针吗?如果是这样,为什么还不这样做呢? 问题答案: 在Go中,没有像C ++中那样的引用类型。
问题内容: 抱歉,这听起来太简单了。我是Java的新手。 这是我用来检查的一些简单代码。当我运行它时,我无法停止它。我以为如果不写任何输入并按,就可以避免循环。 有人可以向我解释在这种情况下如何工作吗? 问题答案: 从System.in读取时,默认情况下是从键盘读取的,这是一个无限的输入流……它的行数与用户希望输入的行数相同。我认为发送EOF的控制序列可能会起作用,例如CTL-Z(或者是CTL-D
我有一个ColdFusion事件网关,有时会在以下行产生错误(其中Local.cur线程是数字1-5): 错误是: 消息:超时值为负。类型:java。lang.IllegalArgumentException 这是StackTrace: Java语言lang.IllegalArgumentException:java的超时值为负。lang.Object。在coldfusion上等待(本机方法)。运
问题内容: 我正在尝试在JPA中使用悲观锁定,而不是针对Postgres数据库使用Hibernate 3。我无法超时锁定-它似乎永远挂着。 这是一个例子: 据我了解,em2应该尝试长达五秒钟(5000毫秒)来获取锁,然后应该抛出异常。而是代码陷入僵局。 如果我在两个不同的线程中运行它,那么我会看到线程2(带有em2)在线程1(em1)释放它后立即获得了锁。因此锁定正在发生,只是永不超时。 我用PE
问题内容: 我正进入(状态 : 当我尝试运行以下 (尽管不完整) 代码段时: 在如下所示: 我正在尝试和班级之间建立联系。 发生此异常的原因是什么? 我创建了两个表并使用以下sql命令: POJO 人 地址 问题答案: 对于多对多关系交换,您需要专用的映射表 6.2.4。价值和多对多关联的集合 即,您需要类似PersonAddress表的内容 其中p_id是对人员表的FK引用,而a_id是对地址表
问题内容: 我正进入(状态 : 当我尝试运行以下 (尽管不完整) 代码段时: 在如下所示: 我正在尝试和班级之间建立联系。 发生此异常的原因是什么? 我创建了两个表并使用以下sql命令: POJO 人 地址 问题答案: 对于ManyToMany Relationshhip,您需要专用的映射表 6.2.4。价值和多对多关联的集合 即,您需要类似PersonAddress表的内容 其中p_id是对人员