Go语言学习(四)| 数组、切片、集合
array
声明数组
array由 [n]<type>
定义,n为array的长度,<type>标示希望存储的内容的类型。例:
var arr [10]int //声明一个int类型的数组
arr[0] = 42 //数组下标是从0开始的
arr[1] = 13 //赋值操作
数组的索引从 0 开始到 length - 1 结束。数组中的所有元素都被自动赋值为数组类型的零值。
NOTE 数组的大小是类型的一部分.
由于长度也是数组类型的一部分,因此 [3]int 与 [4]int 是不同的类型,数组也就不能改变长度。
Go 中的数组是值类型而不是引用类型。
数组之间的赋值是值的赋值,即当把一个数组作为参数传入函数的时候,传入的其实是该数组的副本,而不是它的指针。如果要使用指针,那么就需要用到后面介绍的 slice
类型了。
初始化数组
a := [3]int{1, 2, 3}
初始化数组中 {} 中的元素个数不能大于 [] 中的数字。如果忽略 [] 中的数字不设置数组大小,Go 语言会根据元素的个数来设置数组的大小:
a := [...]int{1, 2, 3}
初始化数组时,可以只初始化部分数组的值,例:
b := [10]int{1, 2, 3} //声明了一个长度为10的int数组,其中前3个元素初始化为1、2、3,其他默认为0。
二维数组
package main
import "fmt"
func main() {
a := [2][2]int{ [2]int{1, 2}, [2]int{3, 4} }
fmt.Println(a)
b := [2][3] int { {1, 2, 3}, {2, 2, 3}}
fmt.Println(b) //[[1 2 3] [2 2 3]]
c := [2][2]int{[...]int{1, 2}, [...]int{3, 4} }
fmt.Println(c)
}
slice
slice 与 array 接近,但是在新的元素加入的时候可以增加长度.slice 是一个指向 array 的指针,这是其与 array 不同的地方;slice 是引用类型,这意味着当赋值某个 slice 到另外一个变量,两个引用会指向同一个 array.
创建切片
基于数组创建
例:
package main
import "fmt"
func main() {
//定义一个 5 个元素的 array,序号从 0 到 4
a := [...]int{1, 2, 3, 4, 5}
fmt.Println("array a is:", a)
//从序号 2 至 3 创建 slice,它包含元素 3, 4 , s1 := a[0:6]; 编译时会报错。因为超出了数组范围
s1 := a[2:4]
fmt.Println("slice s1 is:", s1)
//从序号 1 至 4 创建,它包含元素 2, 3, 4, 5
s2 := a[1:5]
fmt.Println("slice s2 is:", s2)
//用 array 中的所有元素创建 slice,这是 a[0:len(a)] 的简化写法
s3 := a[:]
fmt.Println("slice s3 is:", s3)
//从序号 0 至 3 创建,这是 a[0:4] 的简化写法,得到 1, 2, 3, 4
s4 := a[:4]
fmt.Println("slice s4 is:", s4)
//从 slice s2 创建 slice,注意 s5 仍然指向 array a。a的值变了,slice的值也随之改变
s5 := s2[:]
fmt.Println("slice s5 is:", s5)
fmt.Println("-----------------")
s2[0] = 1
fmt.Println("array a is:", a)
fmt.Println("slice s1 is:", s1)
fmt.Println("slice s2 is:", s2)
fmt.Println("slice s3 is:", s3)
fmt.Println("slice s4 is:", s4)
fmt.Println("slice s5 is:", s5)
}
上述执行输出结果为:
array a is: [1 2 3 4 5]
slice s1 is: [3 4]
slice s2 is: [2 3 4 5]
slice s3 is: [1 2 3 4 5]
slice s4 is: [1 2 3 4]
slice s5 is: [2 3 4 5]
-----------------
array a is: [1 1 3 4 5]
slice s1 is: [3 4]
slice s2 is: [1 3 4 5]
slice s3 is: [1 1 3 4 5]
slice s4 is: [1 1 3 4]
slice s5 is: [1 3 4 5]
直接创建
- 创建一个初始元素个数为5的数组切片,元素初始值为0:
mySlice1 := make([]int, 5)
- 创建一个初始元素个数为5的数组切片,元素初始值为0,并预留10个元素的存储空间:
mySlice2 := make([]int, 5, 10)
- 直接创建并初始化包含5个元素的数组切片:
mySlice3 := []int{1, 2, 3, 4, 5}
slice 和 array 在声明时的区别
- 声明数组时,方括号内写明了数组的长度或使用
...
自动计算长度 - 声明 slice 时,方括号内没有任何字符。
- slice 可以从一个数组或一个已经存在的 slice 中再次声明。slice 通过array[i:j]来获取,其中 i 是数组的开始位置,j 是结束位置,但不包含 array[j] ,它的长度是 j - i .
// 声明一个含有10个元素元素类型为byte的数组
var ar = [10]byte {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'}
// 声明两个含有byte的slice
var a, b []byte
// a指向数组的第3个元素开始,并到第五个元素结束,
a = ar[2:5]
//现在a含有的元素: ar[2]、ar[3]和ar[4]
// b是数组ar的另一个slice
b = ar[3:5]
// b的元素是:ar[3]和ar[4]
slice的一些简便操作
- slice 的默认开始位置是 0,ar[:n] 等价于 ar[0:n]
- slice 的第二个序列默认是数组的长度,ar[n:] 等价于 ar[n:len(ar)]
- 如果从一个数组里面直接获取 slice,可以这样 ar[:],因为默认第一个序列是 0,第二个是数组的长度,即等价于 ar[0:len(ar)]
操作 slice 几个有用的内置函数
-
len
获取 slice 的长度。 -
cap
获取slice的最大容量。 -
append
向 slice 里面追加一个或者多个元素,然后返回一个和 slice 一样类型的 slice。 -
copy
函数 copy 从源 slice 的 src 中复制元素到目标 dst,并且返回复制的元素的个数。
函数 copy
从源 slice src 复制元素到目标 dst,并且返回复制的元素的个数。源和目标可能重叠。复制的数量是 len(src) 和 len(dst) 中的最小值。
注append 函数会改变 slice 所引用的数组的内容,从而影响到引用同一数组的其它 slice。 但当 slice 中没有剩余空间(即(cap - len) == 0)时,此时将动态分配新的数组空间。返回的 slice 数组指针将指向这个空间,而原数组的内容将保持不变;其它引用此数组的 slice 则不受影响。
例:
package main
import "fmt"
func main(){
arr := [10]int{1, 2, 3, 4, 5}
s1 := arr[0:2]
fmt.Println(len(s1)) //输出2
fmt.Println(cap(s1)) //输出10
}
map
声明 map
一般定义 map 的方法是:
map[<from type>] <to type>
例:
var numbers map[string] int
numbers := make(map[string] int)
当只需要声明一个 map 的时候,使用 make 的形式:monthdays := make(map[string]int)例:
package main
import "fmt"
func main(){
monthdays := map[string]int{
"Jan": 31, "Feb": 28, "Mar": 31,
"Apr": 30, "May": 31, "Jun": 30,
"Jul": 31, "Aug": 31, "Sep": 30,
"Oct": 31, "Nov": 30, "Dec": 31,//此处的逗号是必须的
}
for key, val := range monthdays {
fmt.Printf("key = %s, val = %dn", key, val)
}
}
当在 map 中索引(搜索)时,使用方括号。例如打印出 12 月的天数:fmt.Printf("%dn", monthdays["Dec"])这个map就像我们平常看到的表格一样,左边列是 key ,右边列是值
使用map过程中需要注意的几点:
- map 是无序的,所以上述例子每次打印出来的结果顺序都不一样都会不一样,它不能通过 index 获取,而必须通过 key 获取
- map 的长度是不固定的,也就是和 slice 一样,也是一种引用类型
- 内置的 len 函数同样适用于 map,返回 map 拥有的 key 的数量
- map 的值可以很方便的修改,通过 monthdays["Feb"] = 20 可以很容易的把 key 为 Feb 的字典值改为 20
- map 和其他基本类别不同,它不是
Thread-safe
,在多个 goroutine 存取时,必须使用 mutex lock 机制。 Go1.9 以后可用sync.Map
解决这个问题 - map 的初始化可以通过
key:val
的方式初始化值,同时 map 内置有判断是否存在 key 的方式
通过 delete 删除 map 的元素
例:
package main
func main() {
monthdays := map[string]int{
"Jan": 31, "Feb": 28, "Mar": 31,
"Apr": 30, "May": 31, "Jun": 30,
"Jul": 31, "Aug": 31, "Sep": 30,
"Oct": 31, "Nov": 30, "Dec": 31,
}
//删除map中Feb元素
delete(monthdays, "Feb")
//map 有两个返回值,第一个返回值为该 key 的值;
// 第二个返回值,如果不存在 key,那么 ok 为 false,如果存在 ok 为 true
val, ok := monthdays["Feb"]
//判断monthdays["Feb"]是否存在
if ok {
println("Feb is in the map and its value is", val)
} else {
println("Feb isn't in the map")
}
}
输出结果:Feb isn't in the map
例2:
package main
import "fmt"
type Vertex struct {
Lat, Long float64
}
var ms = map[string]Vertex{
"Bell Labs": {40.68433, -74.39967},
"Google": {37.42202, -122.08408},
}
func main() {
fmt.Println(ms)
}
map 也是一种引用类型,如果两个 map 同时指向一个底层,那么一个改变,另一个也相应的改变:
例
package main
import "fmt"
func main() {
m := make(map[string]string)
fmt.Println("map m is: ", m)
m["Hello"] = "Bonjour"
m["World"] = "XXXXX"
fmt.Println("map m is: ", m)
m1 := m
fmt.Println("map m1 is: ", m1)
m1["Hello"] = "Salut" // 现在m["hello"]的值已经是Salut了
fmt.Println("map m is: ", m)
fmt.Println("map m1 is: ", m1)
}
- Hbase 源码分析之 Get 流程及rpc原理
- MOCTF WEB 题解
- HBase行锁与MVCC分析
- HBase行锁探索
- Spring Cloud构建微服务架构:分布式服务跟踪(抽样收集)【Dalston版】
- HBase client访问ZooKeeper获取root-region-server DeadLock问题(zookeeper.ClientCnxn Unable to get data of zn
- zookeeper学习系列:四、Paxos算法和zookeeper的关系
- 有了phonegap你还android吗?
- zookeeper学习系列:三、利用zookeeper做选举和锁
- Spring Cloud构建微服务架构:分布式服务跟踪(收集原理)【Dalston版】
- zookeeper学习系列:二、api实践
- Spring Cloud构建微服务架构:分布式服务跟踪(整合logstash)【Dalston版】
- Spring Cloud构建微服务架构:分布式服务跟踪(整合zipkin)【Dalston版】
- 困扰我多年的Connection reset问题
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Linux查看系统配置常用命令详解
- LNMP下提示File not found问题的解决方法
- Linux 配置SSH免密登录 “ssh-keygen”的基本用法
- 详解Ubuntu 16.04 pycharm设置桌面快捷启动方式
- Linux 7.4上安装配置Oracle 11.2.0.4图文教程
- linux磁盘管理软RAID的实现原理分析和方法分享
- Centos7下Samba服务器配置(实战)
- Linux系统中创建SSH服务器别名的两种方法
- Linux下卸载MySQL8.0版本的操作方法
- Linux服务器上安装Python3的两种方式
- Centos7安装ElasticSearch 6.4.1入门教程详解
- Windows 和 Linux 上Redis的安装守护进程配置方法
- 在Linux系统上安装Spring boot应用的教程详解
- 使用openssl 生成免费证书的方法步骤
- linux cd的含义以及用法