Go语言基础:make,new, len, cap, append, delete方法

uudou · · 8771 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

前面提到不少go的内建函数,这篇文章学习下如何使用。。

make

先拿 make 开刀,可是一开始我就进入了误区,因为我想先找到他的源码,先是发现 src/builtin/builtin.go 中有 func make(Type, size IntegerType) Type ,可是这里只有两个参数,跟我所了解的 make 是个可变参数不太一样,于是我继续搜索源码包是否还有其它 make 函数原型的声明,但都是徒劳。
于是找度娘,一点信息都没有。还是 google 吧,找了一堆的英文解释,发现两个网站解释还可以,具体看How can the golang make function can take three parameters?golang builtin package
总的意思是我在源码找到的 builtin.go 只是一个文档,并不是源代码,这些函数是经过特殊处理的,我们不能直接去使用他们。相对于 make 来说,真正的实现是在 runtime 包里面的 makeslice ,具体怎么做到的,那就要研究编译器了,看汇编是如何实现的。
唉,光make的源码就折腾了我一天的时间,脑袋不够用啊,谁借我一颗脑袋使使。
好,到这里,虽然没有找到 make 的具体源码,但是知道 make 是没有直接的具体源码的,死心了。
make 只能为 slice、ma p或 channel 类型分配内存并初始化,同时返回一个有初始值的 slice、map 或 channel 类型引用,不是指针。具体如何使用 make 呢,官网中 https://golang.org/ref/spec#Making_slices_maps_and_channels 有介绍,这里直接贴出来:

Call             Type T     Result

make(T, n)       slice      slice of type T with length n and capacity n
make(T, n, m)    slice      slice of type T with length n and capacity m

make(T)          map        map of type T
make(T, n)       map        map of type T with initial space for n elements

make(T)          channel    unbuffered channel of type T
make(T, n)       channel    buffered channel of type T, buffer size n

例子:

package main

import "fmt"

func main() {
    slice1 := make([]int, 5)
    slice2 := make([]int, 5, 10)
    fmt.Println(slice1, len(slice1), cap(slice1))
    fmt.Println(slice2, len(slice1), cap(slice2))

    map1 := make(map[string]int)
    map2 := make(map[string]int, 5)

    fmt.Println(map1, len(map1))
    fmt.Println(map2, len(map2))
}

输出:

[0 0 0 0 0] 5 5
[0 0 0 0 0] 5 10
map[] 0
map[] 0

对于make slice而言,有两个概念需要搞清楚:长度跟容量。

  • 容量表示底层数组的大小,长度是你可以使用的大小。
  • 容量的用处在哪?在与当你用 appen d扩展长度时,如果新的长度小于容量,不会更换底层数组,否则,go 会新申请一个底层数组,拷贝这边的值过去,把原来的数组丢掉。也就是说,容量的用途是:在数据拷贝和内存申请的消耗与内存占用之间提供一个权衡。
  • 而长度,则是为了帮助你限制切片可用成员的数量,提供边界查询的。所以用 make 申请好空间后,需要注意不要越界【越 len 】

new

一样也找不到的具体的原型函数,只在src/builtin/builtin.go中有func new(Type) *Type。根据前面查 make 的具体原型的经验,我猜这个这是 new 的函数格式,具体调用应该是类似于 func newint() *int 这种函数,总之应该还是由编译器链接完成的。
但是我们用 new 分配的空间,函数返回的是一个指向新分配的零值的指针。函数格式如下:

func new(Type) *Type

例子

    new1 := new([2]int)
    fmt.Println(new1)
    new1[0] = 1
    new1[1] = 2
    fmt.Println(new1)

输出:

&[0 0]
&[1 2]

len 和 cap

函数 len 格式:

func len(v Type) int
  • 如果 v 是数组:返回的是数组的元素个数
  • 如果 v 是个指向数组的指针:返回的是 *v 的元素个数
  • 如果 v 是 slice 或者 map :返回 v 的元素个数
  • 如果 v 是 channel:the number of elements queued (unread) in the channel buffer;因还不清楚 channel 是啥,就网上直接搬过来

函数 cap 的格式:

func cap(v Type) int
  • 数组:返回的是数组的元素个数,同 len(v)
  • 指向数组的指针:返回的是 *v 的元素个数,同 len(v)
  • slice:返回的是 slice 最大容量,>=len(v)
  • channel: the channel buffer capacity, in units of element

append

append 将元素追加到切片 slice 的末尾,若它有足够的容量,其目标就会重新切片以容纳新的元素。否则,就会分配一个新的基本数组。append 返回更新后的切片,因此必须存储追加后的结果。

append的用法有两种:

slice = append(slice, elem1, elem2)
slice = append(slice, anotherSlice…)

第一种用法中,第一个参数为 slice ,后面可以添加多个参数。
如果是将两个 slice 拼接在一起,则需要使用第二种用法,在第二个 slice 的名称后面加三个点,而且这时候 append 只支持两个参数,不支持任意个数的参数。
个人感觉方法2用得多些
例子1【采用方法1】:

slice1 := make([]int, 5, 10)
slice2 = append(slice2, 5, 6, 7, 8, 9)
fmt.Println(slice2, len(slice2), cap(slice2))
slice2 = append(slice2, 10) 
fmt.Println(slice2, len(slice2), cap(slice2)) 

输出:

[0 1 2 3 4 5 6 7 8 9] 10 10
[0 1 2 3 4 5 6 7 8 9 10] 11 20//注意容量变为了20

例子2【采用方法1】:

    d := []string{"Welcome", "for", "Hangzhou, ", "Have", "a", "good", "journey!"}
    insertSlice := []string{"It", "is", "a", "big", "city, "}
    insertSliceIndex := 3
    d = append(d[:insertSliceIndex], append(insertSlice, d[insertSliceIndex:]...)...)
    fmt.Println(d)

输出:

[Welcome for Hangzhou,  It is a big city,  Have a good journey!]

delete

内建函数 delete 按照指定的键将元素从映射中删除。若 m 为 nil 或无此元素,delete 不进行操作。
函数结构:

func delete(m map[Type]Type1, key Type)

例子

    map1 := make(map[string]int)

    map1["one"] = 1
    map1["two"] = 2
    map1["three"] = 3
    map1["four"] = 4

    fmt.Println(map1, len(map1))
    delete(map1, "two")
    fmt.Println(map1, len(map1))

输出:

map[three:3 four:4 one:1 two:2] 4
map[four:4 one:1 three:3] 3
//map 是无序的,每次打印出来的 map 都会不一样,所以它不能通过 index 获取,而必须通过 key 获取

基本上前面所遇到的函数,这里都有了简单的说明。

参考:

https://wizardforcel.gitbooks.io/golang-stdlib-ref/content/6.html#make
https://golang.org/ref/spec#Making_slices_maps_and_channels
http://www.oschina.net/translate/go-lang-slices


有疑问加站长微信联系(非本文作者)

本文来自:CSDN博客

感谢作者:uudou

查看原文:Go语言基础:make,new, len, cap, append, delete方法

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

8771 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传