崩溃 golang入坑系列
早上(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入坑系列的更多相关文章
- 入坑第二式 golang入坑系列
史前必读: 这是入坑系列的第二式,如果错过了第一式,可以去gitbook( https://andy-zhangtao.gitbooks.io/golang/content/ )点个回放,看个重播.因 ...
- 初生牛犊不怕虎 golang入坑系列
读前必读,下面所有内容都是来自这里. 放到这里的目的,就是为了比对一下,哪里的读者多.平心而论,同样的Markdown,博客园排版真心X看,怎么瞅怎么X看.(X := '难' || X :='耐' | ...
- 维多利亚的秘密 golang入坑系列
原文在gitbook,字字原创,版权没有,转载随意. 在写本文的前一天,2017维密在上海开始了. 为了纪念屌丝界的盛世,特为本节起名维多利亚的秘密.现在的社会,要想出名只有抓眼球.所以写份技术文章, ...
- 分水岭 golang入坑系列
第三式开篇语有些负面, 所以这里就不贴了.有兴趣的自己可以去看看 https://andy-zhangtao.gitbooks.io/golang/content/ .怒发冲冠,意气之作.看完就完了, ...
- 准备冲锋 golang入坑系列
史前摘要: 本来想写读前必读,但连续几篇博文都写读前必读,感觉就没有了新意. 所以换成史前摘要,反正是一个意思. 此摘要的目的仍然是提醒点击而来的同学,本系列最新文章在这里.放到博客园的目的是为了方便 ...
- 坐忘峰 golang入坑系列
读前必读: 本文写于20日,首发于gitbook. 迟到的是日期,没变的是内容. 点击进入 https://andy-zhangtao.gitbooks.io/golang/content/ 可以看到 ...
- 乐呵乐呵得了 golang入坑系列
开场就有料,今天返回去看了看以前的文章,轻松指数有点下降趋势.一琢磨,这不是我的风格呀.一反思,合着是这段时间,脑子里杂七杂八的杂事有点多,事情一多,就忘了快乐.古话说得好:愁也一天,乐也一天,只要还 ...
- 来自朝鲜的问候 golang入坑系列
鸿渐于陆 本想着写满十八式,但按照目前的进度来看,是很难凑够十八式了.所以还是那句话,量力而行,适可而止.能写多少就写多少,我没法保证看完这本golang脱口秀,一定能成为golang大拿.但入了门, ...
- SEO是件贼有意思的事情 golang入坑系列
这两天迷上了SEO.真心看不起百度的竞价排名,但作为一个商业网站,赚钱是一件无可厚非的事情.只做活雷锋,没有大金主是做不长的.做完功课后,发现百度和google的SEO策略又不相同,几乎是无法通用.百 ...
随机推荐
- 测试中出现ERROR StatusLogger No log4j2 configuration file
概述 在hibernate框架搭建完成用log4j2进行测试时,总是出现ERROR StatusLogger No log4j2 configuration file found. Using def ...
- Fiddler使用总结(一)
与后端数据通信是前端日常开发的重要一环,在与后端接口联调的时候往往需要通过查看后端返回的数据进行调试.如果在PC端,Chrome自带的DevTools就已经足够用了,Network面板可以记录所有网络 ...
- (转)spring事务管理几种方式
转自:http://blog.csdn.net/jeamking/article/details/43982435 前段时间对Spring的事务配置做了比较深入的研究,在此之间对Spring的事务配置 ...
- java压缩包上传,解压,预览(利用editor.md和Jstree实现)和下载
java压缩包上传,解压,预览(利用editor.md和Jstree实现)和下载 实现功能:zip文件上传,后台自动解压,Jstree树目录(遍历文件),editor.md预览 采用Spring+Sp ...
- Hadoop完全分布式环境搭建
前言 本文搭建了一个由三节点(master.slave1.slave2)构成的Hadoop完全分布式集群(区别单节点伪分布式集群),并通过Hadoop分布式计算的一个示例测试集群的正确性. 本文集群三 ...
- SharePoint Server 2013 安装篇 - 如何解决无法找到 .net 4.5 的问题
SharePoint Server 2013 在安装前,是不能安装 VS 等会自动安装 .net 4.5.x 以上版本的 .net Framework 的软件的.因为安装了 .net Framewor ...
- IdentityServer4 登录使用数据库
业务场景: IdentityServer4 默认使用TestUser和UserStore,需要模拟和加载所有的用户数据,正式环境肯定不能这样实现,我们想从自己的数据库中读取用户信息,另外,因为 Ide ...
- html网页中加载js脚本 下载下来是乱码(文件编码格式)
问题描述: 在一个index.html网页中,引入了jquery脚本,但是却出现错误,提示$ is not defined <!DOCTYPE html> <html lang=&q ...
- MySQL数据库中文变问号
原文参考:http://www.linuxidc.com/Linux/2017-05/144068.htm 系统是的Ubuntu 16,修改以下配置 1.sudo vi /etc/mysql/my. ...
- css-子div设置margin-top影响父div
父元素的第一个子元素的上边距margin-top如果碰不到有效的border或者padding.就会不断一层一层的找自己父元素,祖先元素,所有需要在父元素设置border,或者padding