早上(11.30)收到邮件,Vultr东京机房网络故障。当时搭建SS时,考虑到了机房故障。所以特意分出了日本和香港两条线路。但千算万算,忘记数据库还在东京机房中。 现在网络故障,SS服务器无法读取数据库中的账号信息。于是乎,主备两条线同时宕了。哭笑两声,没钱去做异地双活,访问量又不大,就这么凑合吧。 我就不信Vultr网络故障是大概率事件。如果很频繁的出故障,用户会用脚去投票的。

铁路警察各管一段,Vultr的故障让Vultr的运维去背锅吧。我们言归正传,继续聊Golang。

在<撸袖子>那节,我们提到了数组。 其中用了一个很少的篇幅说了一下数组的近亲-切片。当时说到数组使用起来不方便,Golang提供了一种更方便的数组使用方式,就是切片。这节中,我们就先来说切片。

先来复习数组的概念,就是一组相同数据类型的集合。在说数组的时候,没有什么动态扩展的方法。只能实现规定好这个数组有多少个元素,然后按照下标进行增删改查。在真实环境中,有很大的局限性。 切片作为数组的近亲,就弥补了这种缺陷。Golang所提供的切片,内置了很多方法来达到数组的动态扩容/缩容。

切片既然是数组的近亲,那声明方式基本上长得很像:

var name []type

name自然是切片名称,type就是数据类型。仅此而已,就完成了一个切片的声明。和数组的声明最大的不同,就在于没有长度限制。这是最常用的声明方式,还有一种文绉绉的声明方式,如下:

name := make([]type, length, capacity)

一瞅就有种学院派的作风。 多了两个参数,length和capacity。这两个概念理解不好,这就是一个大坑。Golang为了让切片有很高的读效率和又不容易出现指针越界,就创造了length和capacity两个属性。

capacity指的是此切片当前指向内存的数据大小。而length指的是当前切片的容量大小,从逻辑上来看,满足这个条件: 0<=length<=capacity。

为什么说这是一个坑? 如果打算用切片操作目标内存的时候,必须小心别append过头,否则就操作到新开启的内存块去了,也要小心别意外覆盖了原slice的值。比如下面:

s := []int{10} //创建一个legnth = capacity = 1 的切片,并且初始化为10
s = append(s,11) //容量不够,翻倍扩容。legnth = capacity = 2,现在是10,11
s = append(s,12) //容量又不够了,再次扩容。legnth =3, capacity = 4,现在是10,11,12
x := append(s, 13) //容量够了,不扩容。legnth = capacity = 4,现在是10,11,12,13
坑来了
y := append(s, 14) //容量够了,不扩容。legnth = capacity = 4,现在是10,11,12,14

但如果你将上面代码输出一下,会看到x和y的值是相同的,都是10.11.12.14。这里面包含了切片的本质。 在Golang官方文档中提及,对切片单独进行append操作,并不会修改切片的内容(也就是单独执行append(s,12)),往往需要将append后的数据重新赋值给源切片,也就是s = append(s,12),这是Golang官方所推荐的用法。 上面的例子中,在x和y那两行,因为s没有发生变化,length=3.所以后面append的值会直接添加到末尾。而返回的又都是同一块内存地址,所以x和y其实指的是同一块内存,因此其内部值也是相同的。 可以来一段代码,把x,y和s的内存地址都输出出来,结果就一目了然了。

如果嫌麻烦,那就用最简单的方式:

var s []int
s = append(s,xxx)

而如果想输出当前的length和capacity,就直接使用len()和cap()两个内置函数。

数组允许存在空数据,切片也当然允许存在空切片。当直接声明一个切片的时候,此时此刻,length = capacity = 0.

var numbers []int
此时此刻 len = 0 cap = 0 slice = []

又该如何判断切片是否为空呢?可以使用length和capacity属性,但不如使用nil来的简单:

numbers == nil

true就表示是空切片,false表示是非空切片。

切片同数组相比,最灵活的方面在于切分子切片。例如可以在代码中,根据业务需要,随时将一个大切片取出任意元素组成一个子切片。看下面:

numbers := []int{0,1,2,3,4,5,6,7,8}
number2 := numbers[:2] // 从0到2,但不包括2.所以是0,1
number3 := numbers[2:5] // 从2到5,但不包括5.所以是2,3,4
number4 := numbers[5:] // 从5到末尾,包括末尾。

上面number2, number3和number4都是子切片,在使用时,需要记住这些子切片都是指向了源切片某一块内存,什么意思?也就是说源切片元素发生了变化,那么子切片也会发生变化。不信? 在上面代码中声明子切片后,任意修改numbers的元素,在看看结果。

如果不想受源切片影响怎么办?使用copy()函数。顾名思义,也就是把重新创建一个切片,自立山头呗。

number5 := make([]int, 2)
copy(number5, numbers[:2])

输出地址之后,就可以看到两者已经完全脱离父子关系,想干嘛就干嘛去吧。

说到最后,需要看一下切片的数据结构了。 我想看到数据结构,上面那些所谓的坑应该就能看明白了。

type slice struct {
array unsafe.Pointer
len int
cap int
}
src/runtime/slice.go

可以看到slice,包含一个指针,一个len变量和一个cap变量。当需要获取length和capacity时,是直接读取的len和cap变量值,不需要再遍历一遍,所以获取长度和容量效率非常高。 而array指向了一块内存,进行append操作时,如果len == cap,则扩容。如果len < cap,那么就是array[len+1]操作。因为golang默认都是值传递,虽然len已经变成len+1了,但原始的slice的len仍然没有变。因此golang才建议,用源切片来接受返回值,这样源切片的len和cap就会同步发生变化。

说实话,这部分脑子里面清楚,但用文字表述的效果欠佳。所以遇到切片时刻记住,用源切片来接受返回值。如果需要子切片,首要需要考虑,是不是需要用copy来复制生成。

转载请保留联系方式 ztao8607@gmail.com

崩溃 golang入坑系列的更多相关文章

  1. 入坑第二式 golang入坑系列

    史前必读: 这是入坑系列的第二式,如果错过了第一式,可以去gitbook( https://andy-zhangtao.gitbooks.io/golang/content/ )点个回放,看个重播.因 ...

  2. 初生牛犊不怕虎 golang入坑系列

    读前必读,下面所有内容都是来自这里. 放到这里的目的,就是为了比对一下,哪里的读者多.平心而论,同样的Markdown,博客园排版真心X看,怎么瞅怎么X看.(X := '难' || X :='耐' | ...

  3. 维多利亚的秘密 golang入坑系列

    原文在gitbook,字字原创,版权没有,转载随意. 在写本文的前一天,2017维密在上海开始了. 为了纪念屌丝界的盛世,特为本节起名维多利亚的秘密.现在的社会,要想出名只有抓眼球.所以写份技术文章, ...

  4. 分水岭 golang入坑系列

    第三式开篇语有些负面, 所以这里就不贴了.有兴趣的自己可以去看看 https://andy-zhangtao.gitbooks.io/golang/content/ .怒发冲冠,意气之作.看完就完了, ...

  5. 准备冲锋 golang入坑系列

    史前摘要: 本来想写读前必读,但连续几篇博文都写读前必读,感觉就没有了新意. 所以换成史前摘要,反正是一个意思. 此摘要的目的仍然是提醒点击而来的同学,本系列最新文章在这里.放到博客园的目的是为了方便 ...

  6. 坐忘峰 golang入坑系列

    读前必读: 本文写于20日,首发于gitbook. 迟到的是日期,没变的是内容. 点击进入 https://andy-zhangtao.gitbooks.io/golang/content/ 可以看到 ...

  7. 乐呵乐呵得了 golang入坑系列

    开场就有料,今天返回去看了看以前的文章,轻松指数有点下降趋势.一琢磨,这不是我的风格呀.一反思,合着是这段时间,脑子里杂七杂八的杂事有点多,事情一多,就忘了快乐.古话说得好:愁也一天,乐也一天,只要还 ...

  8. 来自朝鲜的问候 golang入坑系列

    鸿渐于陆 本想着写满十八式,但按照目前的进度来看,是很难凑够十八式了.所以还是那句话,量力而行,适可而止.能写多少就写多少,我没法保证看完这本golang脱口秀,一定能成为golang大拿.但入了门, ...

  9. SEO是件贼有意思的事情 golang入坑系列

    这两天迷上了SEO.真心看不起百度的竞价排名,但作为一个商业网站,赚钱是一件无可厚非的事情.只做活雷锋,没有大金主是做不长的.做完功课后,发现百度和google的SEO策略又不相同,几乎是无法通用.百 ...

随机推荐

  1. 安装debian 9.1后,中文环境下将home目录下文件夹改为对应的英文

    安装了debian 9.1后,中文环境下home目录下文件夹显示的是中文,相当不方便cd命令,改为对应的英文吧,需要用到的软件xdg-user-dirs-gtk #安装需要的软件 sudo apt i ...

  2. Tomcat Java.OutOfMemoryError : PermGen Space异常

    背景:前些日子更新公司多年前一个旧平台发布到Tomcat上之后,频繁收到网站许多模块无法正常使用的反汇. 测试过程中发现平台发布一段时间后,访问相关网页出现如下500页面 解决方案:PermGen s ...

  3. Cocos 2d-X Lua 游戏添加苹果内购(一) 图文详解准备流程

    事前准备 最近给游戏添加了苹果的内购,这一块的东西也是刚刚做完,总结一下,其实这里不管是游戏还是我们普通的App添加内购这一块的东西都是差不多的,多出来的部分就是我们Lua和OC的交互的部分,以前刚开 ...

  4. Chrome 62 的大坑:修改密码后始终使用保存的旧密码登录

    最近有用户向我们反馈,修改密码后,怎么也登录不了我们网站,总是提示密码错误.用户确认密码肯定没错,通过用户发给我们的操作截图看,用户修改密码的操作也没问题. 开始我们没能重现出这个问题,我们检查了相关 ...

  5. CentOs 系统启动流程相关

    CentOS的启动流程 1)加载BIOS 的硬件信息,获取第一个启动设备 2)读取第一个启动设备MBR 的引导加载程序(grub) 的启动信息 3)加载核心操作系统的核心信息,核心开始解压缩,并尝试驱 ...

  6. 在mac OS10.10下安装 cocoapods遇到的一些问题

    今天有个朋友问了我一个问题:为什么我安装cocoapods不成功,报 sh: line 1: 997 Abort trap: 6 /Applications/Xcode.app/Contents/De ...

  7. OpenCV4Android

    前文曾详细探讨了关于OpenCV的使用,原本以为天下已太平.但不断有人反应依然配不好OpenCV4Android,不能得心应手的在Android上使用OpenCV,大量的精力都浪费在摸索配置上.尤其是 ...

  8. Windows环境下Android Studio安装和使用教程

    Windows环境下Android Studio安装和使用教程 来源: http://www.cnblogs.com/liuhongfeng/archive/2015/12/30/5084896.ht ...

  9. 【Win 10 应用开发】UI Composition 札记(二):基本构件

    在上一篇中,老周用一个示例,演示了框架视图的创建过程,在本篇中,老周将给大伙伴们说一下 Composition 构建 UI 的一些“零件”. UI Composition 有一个核心类——对,就是 C ...

  10. CodeForces - 556B Case of Fake Numbers

    //////////////////////////////////////////////////////////////////////////////////////////////////// ...