早上(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. SpringMVC + Spring + Mybatis+ Redis +shiro以及MyBatis学习

    SpringMVC + Spring + Mybatis+ Redis +shiro http://www.sojson.com/shiro MyBatis简介与配置MyBatis+Spring+My ...

  2. Windows删除文件时找不到该项目

    当在Windows删除文件时出现找不到该项目或者显示该文件不在磁盘中,可以尝试以下方法: 在要删除文件的同级目录下 新建一文本文档,将下列代码复制到文档中,将文档保存为后缀名为.bat的文档(名字随意 ...

  3. jQuery在项目中的应用

    版权声明:本文为博主原创文章,未经博主允许不得转载.(转载需注明出处 http://www.cnblogs.com/yanfei1819/p/7743661.html) [摘要]   最近在项目中应用 ...

  4. spring MVC 环境搭建

    绿色版Spring MVC(单纯的springMVC) 一.导包,为了获取请求数据多添加一个包 二.web.xml配置 <?xml version="1.0" encodin ...

  5. linux云服务器常用设置

    前面的话 由于在云服务器上配置自己的网站,将Linux里的常用设置记录如下 更改shell 默认地, ubuntu系统默认的shell是dash,但更常用的shell是bash 通过下面命令可以将da ...

  6. linux GCC 编译多个.c/.h文件

    基本认识: #include <xxx>:首先去系统目录中找头文件,如果没有在到当前目录下找.像标准的头文件 stdio.h.stdlib.h等用这个方法. #include " ...

  7. Hive中常用的查询命令

    日志数据的统计处理在这里反倒没有什么特别之处,就是一些 SQL 语句而已,也没有什么高深的技巧,不过还是列举一些语句示例,以示 hive 处理数据的方便之处,并展示 hive 的一些用法. a)    ...

  8. c++学习笔记---01---C++语言与OO思想介绍

    C++语言与OO思想介绍 C++的特点与OO思想 C语言有一个优点,即它的速度可以很快.写出来的程序可以很精练.简单.小巧,不用为了解决某个问题环绕太平洋一大圈. 但如果将C和C++相比较,C++就经 ...

  9. SeaJS之shim插件:解决非cmd规范的插件与sea的区别

    SeaJS 中的模块默认都遵守 CMD 规范,但现实中已存在大量普通 JavaScript 类库,比如 jQuery.Underscore 等.使用 shim 插件,可以将这些普通 JS 文件转换成 ...

  10. media query 单位

    使用em 判断的media query 在用户缩放的时候不会被破坏,使用em更加合适内容为王的页面趋势   内容的容器需要根据内容而按比例设置,就像line-height:1.14em,文字大小的1. ...