《Go语言实战》笔记之第四章 ----数组、切片、映射
原文地址:
http://www.niu12.com/article/11
####数组
数组是一个长度固定的数据类型,用于存储一段具有相同的类型的元素的连续块。
数组存储的类型可以是内置类型,如整型或者字符串,也可以是某种结构类型,
其占用的内存是连续分配的.
由于内存连续,CPU能把正在使用的数据缓存更久的时间。
而且内存连续很容易计算索引, 可以快速迭代数组里的所有元素。
声明:
两个要素:长度和类型
声明数组时需要指定内部存储的数据的类型, 以及需要存储的元素的数量,
这个数量也称为数组的长度
// 声明一个包含 5 个元素的整型数组
var array [5]int
一旦声明,数组里存储的数据类型和数组长度就都不能改变,元素为零值
如果需要存储更多的元素,
就需要先创建一个更长的数组,再把原来数组里的值复制到新数组里
// 声明一个包含 5 个元素的整型数组并初始化
array := [5]int{10, 20, 30, 40, 50}
...可替代长度,Go 语言会根据初始化时数组元素的数量来确定该数组的长度
// 容量由初始化值的数量决定
array := [...]int{10, 20, 30, 40, 50}
// 声明一个有 5 个元素的数组
// 用具体值初始化索引为 1 和 2 的元素
// 其余元素保持零值
array := [5]int{1: 10, 2: 20}
array[3] = 30
array值为: [0, 10, 20, 30 0]
指针数组:所有元素都是指针的数组
(指向整型的指针叫整型指针)
(指向字符串的指针叫字符串指针)
(......)
声明包含 5 个元素的指向整数的数组
// 用整型指针初始化索引为 0 和 1 的数组元素
array := [5]*int{0: new(int), 1: new(int)}
// 为索引为 0 和 1 的元素赋值
*array[0] = 10
*array[1] = 20
array值为: [0xc0420080a8 0xc0420080c0 <nil> <nil> <nil>]
<code>
array := [5]*int{0: new(int), 1: new(int)}
// 为索引为 0 和 1 的元素赋值
*array[0] = 10
*array[1] = 20
for _, p := range array {
if p != nil {
fmt.Println(*p)
} else {
fmt.Println(p)
}
}
// 输出 10 20 nil nil nil
</code>
数组的比较:
数组变量的类型包括 数组长度 和每个元素的 类型 。
只有这两部分都相同的数组, 才是类型相同的数组,才能互相赋值
var array1 [5]string
array2 := [5]string{"Red", "Blue", "Green", "Yellow", "Pink"}
array1 = array2 // ok
var array3 [5]*string
array3 = array2 // error
在函数间传递数组:
根据内存和性能来看,在函数间传递数组是一个开销很大的操作。
在函数之间传递变量时,总是以值的方式传递的。
如果这个变量是一个数组,意味整个数组,不管有多长,都会完整复制,并传递给函数
最佳实践:传递数组的指针,这个操作会更有效地利用内存,性能也更好。
要意识到,因为现在传递的是指针,所以如果改变指针指向的值,会改变共享的内存
####切片slice
切片是动态数组,可以按需自动增长和缩小。
切片的动态增长是通过内置函数 append 来实现的。
这个函数可以快速且高效地增长切片。
还可以通过对切片再次切片来缩小一个切片的大小。
因为切片的底层内存也是在连续块中分配的,
所以切片还能获得索引、迭代以及为垃圾回收优化的好处。
声明:
两个必选要素: 类型与长度
一个可选要素: 容量
// 创建一个字符串切片
// 其长度和容量都是 5 个元素
slice := make([]string, 5)
分别指定长度和容量时,创建的切片,底层数组的长度是指定的容量,
但是初始化后并不能访问所有的数组元素
// 创建一个整型切片
// 其长度为 3 个元素,容量为 5 个元素
slice := make([]int, 3, 5)
for k := range slice {
fmt.Println(k) // 0 1 2
}
可以访问 3 个元素,而底层数组拥有 5 个元素。
剩余的 2 个元素可以在后期操作中合并到切片,可以通过切片访问这些元素
如果基于这个切片创建新的切片,新切片会和原有切片共享底层数组
len(array) <= cap(array)
使用切片字面量创建切片,同数组,只是不需要规定长度:
初始的长度和容量会基于初始化时提供的元素的个数确定
// 创建字符串切片
// 其长度和容量都是 5 个元素
slice := []string{"Red", "Blue", "Green", "Yellow", "Pink"}
// 创建一个整型切片
// 其长度和容量都是 3 个元素
slice := []int{10, 20, 30}
// 设置初始长度和容量
// 创建字符串切片
// 使用空字符串初始化第 100 个元素
slice := []string{99: ""}
切片与数组的区别:
// 创建有 3 个元素的整型数组
array := [3]int{10, 20, 30}
// 创建长度和容量都是 3 的整型切片
slice := []int{10, 20, 30}
// 创建 nil 整型切片
var slice []int
// true
fmt.Println(slice == nil)
// 使用 make 创建空的整型切片
slice2 := make([]int, 0)
// false
fmt.Println(slice2 == nil)
// 使用切片字面量创建空的整型切片
slice3 := []int{}
// false
fmt.Println(slice3 == nil)
切片赋值:
// 创建一个整型切片
// 其容量和长度都是 5 个元素
slice := []int{10, 20, 30, 40, 50}
// 改变索引为 1 的元素的值
slice[1] = 25
使用切片创建切片:
// 创建一个整型切片
// 其长度和容量都是 5 个元素
slice := []int{10, 20, 30, 40, 50}
// 创建一个新切片
// 其长度为 2 个元素,容量为 4 个元素 [i:j]包i不包j
newSlice := slice[1:3]
fmt.println(newSlice) // [20, 30]
第一个切片 slice 能够看到底层数组全部 5 个元素的容量,
不过之后的 newSlice 就看不到。
对于 newSlice ,底层数组的容量只有 4 个元素。
newSlice 无法访问到它所指向的底层数组的第一个元素之前的部分。
所以,对 newSlice 来说,之前的那些元素就是不存在的。
现在两个切片共享同一个底层数组。
如果一个切片修改了该底层数组的共享部分,另一个切片也能感知到
newSlice[0] = 1
fmt.Println(slice, newSlice) // [10 1 30 40 50] [1 30]
对底层数组容量是 k 的切片 slice[i:j]来说
长度: j - i
容量: k - i
对于 slice[i:j:k] 或 [2:3:4]
长度: j – i 或 3 - 2 = 1
容量: k – i 或 4 - 2 = 2
如果k - i大于可用容量,error: slice bounds out of range
切片只能访问到其长度内的元素。
试图访问超出其长度的元素将会导致语言运行时异常。
与切片的容量相关联的元素只能用于增长切片
切片增长:
用 append,需要一个被操作的切片和一个要追加的值
当
append 调用返回时,会返回一个包含修改结果的新切片。
函数 append 总是会增加新切片的长度,而容量有可能会改变,
也可能不会改变,这取决于被操作的切片的可用容量
// 创建一个整型切片
// 其长度和容量都是 5 个元素
slice := []int{10, 20, 30, 40, 50}
// 创建一个新切片
// 其长度为 2 个元素,容量为 4 个元素
newSlice := slice[1:3]
// 使用原有的容量来分配一个新元素
// 将新元素赋值为 60
newSlice = append(newSlice, 60)
// [10 20 30 60 50] [20 30 60]
fmt.Println(slice, newSlice)
因为 newSlice 在底层数组里还有额外的容量可用,
append 操作将可用的元素合并到切片的长度,
并对其进行赋值。由于和原始的 slice 共享同一个底层数组,
slice 中索引为 3 的元素的值也被改动了。
newSlice = append(newSlice, 60)
newSlice = append(newSlice, 60)
// 4
fmt.Println(cap(newSlice))
newSlice = append(newSlice, 60)
// 8
fmt.Println(cap(newSlice))
// [10 20 30 60 60] [20 30 60 60 60]
fmt.Println(slice, newSlice)
如果切片的底层数组没有足够的可用容量,
append 函数会创建一个新的底层数组,
将被引用的现有的值复制到新数组里,再追加新的值
函数 append 会智能地处理底层数组的容量增长。在切片的容量小于 1000 个元素时,总是
会成倍地增加容量。一旦元素个数超过 1000,容量的增长因子会设为 1.25,也就是会每次增
加 25%的容量。随着语言的演化,这种增长算法可能会有所改变。
内置函数 append 会首先使用可用容量。一旦没有可用容量,会分配一个
新的底层数组。这导致很容易忘记切片间正在共享同一个底层数组。
一旦发生这种情况,对切片进行修改,很可能会导致随机且奇怪的问题。
对切片内容的修改会影响多个切片,却很难找到问题的原因。
如果在创建切片时设置切片的容量和长度一样,
就可以强制让新切片的第一个 append 操作创建新的底层数组,
与原有的底层数组分离。
新切片与原有的底层数组分离后,可以安全地进行后续修改
内置函数 append 也是一个可变参数的函数。
这意味着可以在一次调用传递多个追加的值。
如果使用...运算符,可以将一个切片的所有元素追加到另一个切片里
// 创建两个切片,并分别用两个整数进行初始化
s1 := []int{1, 2}
s2 := []int{3, 4}
// 将两个切片追加在一起,并显示结果 [1 2 3 4]
fmt.Printf("%v\n", append(s1, s2...))
关键字 range配合关键字 for 来迭代切片里的元素
当迭代切片时,关键字 range 会返回两个值。
第一个值是当前迭代到的索引位置,
第二个值是该位置对应元素值的一份副本而不是直接返回对该元素的引用
可以使用空白标识符来忽略值
有两个特殊的内置函数 len 和 cap,可以用于处理数组、切片和通道
函数传递切片:
在函数间传递切片就是要在函数间以值的方式传递切片。
由于切片的尺寸很小,在函数间复制和传递切片成本也很低
在 64 位架构的机器上,一个切片需要 24 字节的内存:
指针字段需要 8 字节,长度和容量字段分别需要 8 字节
由于与切片关联的数据包含在底层数组里,不属于切片本身,
所以将切片复制到任意函数的时候,对底层数组大小都不会有影响。
复制时只会复制切片本身,不会涉及底层数组
####映射
映射是一种数据结构,用于存储一系列无序的键值对。
映射里基于键来存储值,映射功能强大的地方是,能够基于键快速检索数据。
映射的实现使用了散列表,所以映射是无序的集合
映射的散列表包含一组桶。在存储、删除或者查找键值对的时候,
所有操作都要先选择一个桶。把操作映射时指定的键传给映射的散列函数,
就能选中对应的桶。这个散列函数的目的是生成一个索引,
这个索引最终将键值对分布到所有可用的桶里。
随着映射存储的增加,索引分布越均匀,访问键值对的速度就越快
映射通过合理数量的桶来平衡键值对的分布。
/ 创建一个映射,键的类型是 string,值的类型是 int
dict := make(map[string]int)
// 创建一个映射,键和值的类型都是 string
// 使用两个键值对初始化映射
dict := map[string]string{"Red": "#da1337", "Orange": "#e95a22"}
映射的键可以是任何值。这个值的类型可以是内置的类型,也可以是结构类型,
只要这个值可以使用==运算符做比较
映射赋值:
// 创建一个空映射,用来存储颜色以及颜色对应的十六进制代码
colors := map[string]string{}
// 将 Red 的代码加入到映射
colors["Red"] = "#da1337"
从映射获取值并判断键是否存在
// 获取键 Blue 对应的值
value, exists := colors["Blue"]
通过键来索引映射时,即便这个键不存在也总会返回一个值。
在这种情况下,返回的是该值对应的类型的零值。
迭代映射里的所有值和迭代数组或切片一样,使用关键字 range
如果想把一个键值对从映射里删除,就使用内置的 delete 函也就是会每次增数
当传递映射给一个函数,并对这个映射做了修改时,
所有对这个映射的引用都会察觉到这个修改
这个特性和切片类似,保证可以用很小的成本来复制映射
《Go语言实战》笔记之第四章 ----数组、切片、映射的更多相关文章
- java并发编程实战笔记---(第四章)对象的组合
4.1设计线程安全的类 包含三个基本要素: 1.找出构成对象状态的所有变量 2.找出约束状态变量的不变性条件 2.简历对象状态的并发访问管理策略 对象的状态: 域 基本类型所有域, 引用类型包括被引用 ...
- Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第四章:Direct 3D初始化
原文:Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第四章:Direct 3D初始化 学习目标 对Direct 3D编程在 ...
- 【NPDP笔记】第四章 文化组织与团队
此为临时链接,仅用于预览,将在短期内失效.关闭 [NPDP笔记]第四章 文化组织与团队 小康 小康哥的产品之路 9月6日 4.1 文化和氛围对创新的重要性 文化:信念,价值观,假设,与期望 氛围:直接 ...
- win32多线程程序设计笔记(第四章下)
上一笔记讲了同步机制中的临界区域(Critical Sections).互斥器(Mutexes),下面介绍同步机制中的另外两种. 信号量(Semaphores) 举个例子: 现在有人要租车,接待他的代 ...
- Java语言程序设计(基础篇) 第四章 数学函数、字符和字符串
第四章 数学函数.字符和字符串 4.2 常用数学函数 方法分三类:三角函数方法(trigonometric method).指数函数方法(exponent method)和服务方法(service m ...
- 05 技术内幕 T-SQL 查询读书笔记(第四章)
第四章 子查询:在外部查询内嵌套的内部查询(按照期望值的数量分为,标量子查询 scalar subqueries,多值子查询multivalued subqueries)(按照子查询对外部查询的依赖性 ...
- Linux内核分析 读书笔记 (第四章)
第四章 进程调度 调度程序负责决定将哪个进程投入运行,何时运行以及运行多长时间.进程调度程序可看做在可运行态进程之间分配有限的处理器时间资源的内核子系统.只有通过调度程序的合理调度,系统资源才能最大限 ...
- 《深入理解java虚拟机》读书笔记三——第四章
第四章 虚拟机性能监控与故障处理工具 1.JDK命令行工具 jps命令: 作用:列出正在运行的虚拟机进程. 格式:jps [option] [hostid] 选项:-q 只输出LVMID(Local ...
- 读书笔记,《Java 8实战》,第四章,引入流
集合是Java中使用最多的API,但集合操作却远远算不上完美.主要表现在两点, 第一,集合不能让我们像数据库的SQL语言一样用申明式的语言指定操作: 第二,现在的集合API无法让我们 ...
随机推荐
- REST,Web 服务,REST-ful 服务
介绍 REpresentational State Transfer (REST) 是一种架构原则,其中将 web 服务视为资源,可以由其 URL 唯一标识.RESTful Web 服务的关键特点是明 ...
- JavaScript中对象的属性类型
JavaScript中,对象的属性有两种:数据属性和访问器属性. 数据属性 特性: 数据属性包括一个数据值的位置.在这个位置可以读取和写入值.数据属性有4个特性. [[configurable]]:可 ...
- Mysql 数据库学习笔记05 触发器
一.触发器 * 是由时间来出发某个操作,这些事件可以包括 insert.update.delete.等语句.当执行这些操作时,就会触发对应的操作. * 创建一个执行语句的触发器: create tri ...
- C#判断目录是否为隐藏
判断方法: DirectoryInfo di = new DirectoryInfo(path); if ((di.Attributes & FileAttributes.Hidden) == ...
- php写入和读取文件内容
function read_file($filename){ // $filename = "/usr/local/something.txt"; $handle = @fopen ...
- bzoj 1443 二分图博弈
这种两个人轮流走,不能走 走过的格子的大都是二分图博弈... #include<bits/stdc++.h> #define LL long long #define fi first # ...
- android studio偏好设置
1.主题设置,可以选择白色主题及黑色主题 2.代码字体大小 3.生成新的主题 主题命名 4.加入代码时,自动引用库 5.合作菜单生成菜码 6.命名空间设置 字段设置为大写,静态字段设置为小写 SDK设 ...
- PTA L2-006 树的遍历-二叉树的后序遍历+中序遍历,输出层序遍历 团体程序设计天梯赛-练习集
L2-006 树的遍历(25 分) 给定一棵二叉树的后序遍历和中序遍历,请你输出其层序遍历的序列.这里假设键值都是互不相等的正整数. 输入格式: 输入第一行给出一个正整数N(≤),是二叉树中结点的 ...
- HRBUST 1311 火影忍者之~忍者村
求连通块. $ABC$之间建好边,然后计算连通块的个数. #pragma comment(linker, "/STACK:1024000000,1024000000") #incl ...
- 32、Flask实战第32天:优化json数据的返回
接着上节,我们通过jsonify返回json数据非常方便 ... return jsonify({"code": 400, "message": message ...