Golang 数组 切片 字典 基本知识点
数组
数组的声明
var arrayName [arraySize]dataType
eg:
var array1 [5]int
在声明数组时,必须指定数组名,数组长度和数组元素的类型。
数组的初始化
当数组定义好以后,如果没有给数组元素指定值,则所有元素被自动初始化为类型所对应的零值
var array1 [5]int
// [0 0 0 0 0]
变量的类型零值
在Go语言中,当一个变量被定义为某一种类型后,Go语言会自动初始化其值为零(Zero Value)
零值并不等于空值,而是当变量被声明为某种类型后的默认值
类型 | 类型零值 |
---|---|
bool | false |
int, int8, int16, int32, int64 | 0 |
uint, uint8, uint16, uint32, uint64 | 0 |
float32, float64 | 0 |
complex64, complex128 | 0+0i |
byte, rune | 0 |
string | "" |
pointer, function, interface, slice, channel, map | nil |
规律:
- 数值类型的默认值为0
- 布尔类型的默认值为false
- 字符串类型会初始化为空字符串
- 其他类型为nil,表示没有分配内存地址,使用前必须人为分配内存地址
(1)在定义数组时对数组元素赋初值
var array1 = [5]int{0, 1, 2, 3, 4}
// [0 1 2 3 4]
注意:花括号中的元素个数不得超过arraySize
(2)可以只给一部分元素赋初值
var array1 = [5]int{0, 1, 2}
// [0 1 2 0 0]
注意:未赋值的默认初始化为类型零值
(3)可以由初始化列表决定数组长度
var array1 = [...]int{0, 1, 2, 4, 5, 6}
// [0 1 2 4 5 6]
注意:
该例中数组长度为 ··· 标识符,表示没有指定数组长度,数组长度由初始化列表决定
··· 标识符不可省略,否则将变成后文将要讲的切片
(4)按照下标初始化元素
var array1 = [5]int{0: 5, 4: 5}
// [5 0 0 0 5]
数组元素的访问和遍历
访问
数组元素按照下标进行访问,下标从0开始,可以是整型常量或者整型表达式
var array1 = [8]int{1, 2, 3, 4, 5, 6, 7, 8}
fmt.Println(array1[0], array1[6], array1[2*3])
// 1 7 7
遍历
(1)for语句
var array1 = [8]int{1, 2, 3, 4, 5, 6, 7, 8}
for i := 0; i < 8; i++ {
fmt.Printf("%d ", array1[i])
}
// 1 2 3 4 5 6 7 8
(2)for range语句
var array1 = [3]int{1, 2, 3}
for i, v := range array1 {
fmt.Printf("index is %d, value is %d.\n", i, v)
}
// index is 0, value is 1.
// index is 1, value is 2.
// index is 2, value is 3.
range 具有两个返回值,第一个返回值i是数组元素的下标,第二个返回值v是数组元素的值
二维数组
var array2 = [3][4]int{{1, 2, 3, 4}, {5, 6, 7, 8}, {}} // 三行四列
fmt.Println(array2)
// [[1 2 3 4] [5 6 7 8] [0 0 0 0]]
切片
切片(slice)是数组的一个引用,它会生成一个指向数组的指针,并通过切片长度关联到底层数组部分或者全部元素。
切片中的元素是可以动态增加,删除的,所以切片通常被用来实现变长数组。
切片的数据结构原型定义如下(Go/src/runtime/slice.go
)
type slice struct {
array unsafe.Pointer // 指向被引用的底层数组的指针
len int // 切片中元素的个数
cap int // 切片分配的存储空间
}
切片的声明与创建
(1)直接创建切片
var slice1 = []int{1, 2, 3, 4}
fmt.Println(slice1, len(slice1), cap(slice1))
// [1 2 3 4] 4 4
(2)基于底层数组创建切片
在创建切片时,可以基于一个底层数组,切片可以只使用数组的一部分元素,或者所有元素
var slice1 []int // 注意和数组的区别
// 当一个切片定义好以后,如果还没有被初始化,默认值为nil
fmt.Println(slice1, len(slice1), cap(slice1))
// 使用len()获取切片的长度,使用cap()获取切片的容量
// [] 0 0
// 使用slice1 = array1[start:end]引用底层数组来初始化切片
// 即切片引用数组元素由array1[start]到array2[end],但不包括array1[end]
// start默认值为0,end默认值为len(array1)
var array1 = [5]int{1, 2, 3, 4, 5}
slice1 = array1[:]
fmt.Println(slice1, len(slice1), cap(slice1))
// [1 2 3 4 5] 5 5
var slice2 []int
slice2 = array1[1:4]
fmt.Println(slice2, len(slice2), cap(slice2))
// [2 3 4] 3 4
(3)使用make函数创建切片
var sliceName = make(sliceType,len,cap)
// 使用make()函数创建切片时,可以指定切片的元素个数,并且为切片元素预留存储空间
// 在后文切片的操作部分,我们会讲解指定(预估)切片容量的好处
eg:
var slice1 = make([]int, 3, 5) // 切片的元素个数为3,切片的容量为5
fmt.Println(slice1, len(slice1), cap(slice1))
// [0 0 0] 3 5
切片元素的访问和遍历
这个和数组元素的访问以及遍历是一样的
切片的内置操作
(1)len()
获取切片中元素的个数
(2)cap()
获取切片的容量大小
(3)append()
使用append()函数向切片尾部添加新元素,这些元素保存到底层数组
append()并不会影响原来切片的属性,它返回变更后新的切片对象
var slice1 = make([]int, 3, 5)
fmt.Println(slice1, len(slice1), cap(slice1)) // [0 0 0] 3 5
slice2 := append(slice1, 1) // 返回一个新的切片对象
fmt.Println(slice1, len(slice1), cap(slice1)) // slice1没有发生发生变化
// [0 0 0] 3 5
fmt.Println(slice2, len(slice2), cap(slice2)) // [0 0 0 1] 4 5
slice2 = append(slice2, 2) // 将新的切片对象重新赋值给slice2
fmt.Println(slice2, len(slice2), cap(slice2)) // [0 0 0 1 2] 5 5
注意:
与数组相比,切片多了一个容量的概念,即切片中元素的个数和分配的存储空间是两个不同的值
如果在append后,没有超过切片的容量大小,哪么容量不会发生变化
如果append后,超过了容量大小,则底层会重新分配一块“够大”的内存,具体的扩容机制可以在
Go\src\runtime\slice.go
文件中的growslice
函数中看到正如前面我们在使用make()函数创建切片时,如果我们能够预计出合理的容量大小(太大浪费内存空间,太小会不断的扩容),哪么我们在进行切片的append时,可能不会发生扩容,也就避免了切片元素的复制,减少了开销。
(4)copy()
// The copy built-in function copies elements from a source slice into a
// destination slice. (As a special case, it also will copy bytes from a
// string to a slice of bytes.) The source and destination may overlap. Copy
// returns the number of elements copied, which will be the minimum of
// len(src) and len(dst).
func copy(dst, src []Type) int
var slice1 = []int{1, 2, 3, 4, 5, 6}
var slice2 = make([]int, 3, 5)
num := copy(slice2, slice1)
fmt.Println(slice1) // [1 2 3 4 5 6]
fmt.Println(slice2) // [1 2 3]
fmt.Println(num) // 3
字典
Map是一种特殊的数据结构,由一对无序的数据项组成,被称为键值对(Key-value Pair)
字典的声明
var mapName map[keyType]valueType
eg:
var map1 map[string]int
// 字典声明好后必须经过初始化或者创建才能使用
// 未初始化或创建的字典值为nil
字典的初始化和创建
(1)使用 {}
操作符对字典进行初始化
var map1 map[string]int
map1["egg"] = 1 // 对字典没有初始化就尝试增加数据,编译时就会报错
panic: assignment to entry in nil map
var map1 = map[string]int{} // !!!使用{}来初始化map,也就意味着系统给map1分配了存储空间
map1["apple"] = 2 // 使用=向map中添加元素
fmt.Println(map1) // map[apple:2]
var map1 = map[string]int{"microsoft":0} //在初始化的时候就指定key-value元素
(2)使用 make()
函数来创建字典
var map1 = make(map[string]int) // 如下引用所示,我们可以指定map的长度,也可以省略
// 注意:make创建map是不可指定cap属性,当然cap()函数也不可用于map类型
map1["egg"] = 1
fmt.Println(map1) // map[egg:1]
// Map: An empty map is allocated with enough space to hold the
// specified number of elements. The size may be omitted, in which case
// a small starting size is allocated.
func make(t Type, size ...IntegerType) Type
字典的访问和遍历
(1)通过key来访问Value
var map1 = map[string]int{"Microsoft": 0}
map1["egg"] = 1
map1["apple"] = 2
fmt.Println(map1) // map[Microsoft:0 apple:2 egg:1]
// 通过Key来访问value
fmt.Println(map1["egg"]) // 1
(2)字典项查找
v,ok := mapName[Key]
// 如果查找的key值存在,则将key对应的value值赋予v,ok为true
// 如果查找的key值不存在,则将v为0,ok为false
eg:
if _, ok := map1["agg"]; ok == false {
map1["agg"] = 666
}
fmt.Println(map1) // map[Microsoft:0 agg:666 apple:2 egg:1]
(3)使用for,range遍历
for k, v := range map1 {
fmt.Printf("key is %s,value is %d.\n", k, v)
}
// key is Microsoft,value is 0.
// key is egg,value is 1.
// key is apple,value is 2.
// key is agg,value is 666.
注意:map是无序的,所以遍历的结果也是无序的。
字典的内置操作
(1)len()
获取字典元素的个数
(2)delete()
// The delete built-in function deletes the element with the specified key
// (m[key]) from the map. If m is nil or there is no such element, delete
// is a no-op.
func delete(m map[Type]Type1, key Type)
var map1 = map[string]int{"Microsoft": 0}
map1["egg"] = 1
map1["apple"] = 2
fmt.Println(map1) // map[Microsoft:0 apple:2 egg:1]
delete(map1, "egg") // key存在于map1中
fmt.Println(map1) // map[Microsoft:0 apple:2]
delete(map1, "none") // key不存在于map1中,没有任何作用,不会panic
fmt.Println(map1) // map[Microsoft:0 apple:2]
var map2 map[string]int
delete(map2, "nil") // map2为nil,也没有任何作用,不会panic
fmt.Println(map2) // map[]
总结
小结
本文简述了Go语言中最基本,最常用的三种数据结构的基本知识点,在掌握基础后,我们可以针对自己的兴趣点,去深入的研究底层的知识。
参考资料
Golang 数组 切片 字典 基本知识点的更多相关文章
- go 数组 切片 字典 结构体
数组 ##数组的定义与赋值: 1. var num [3]int num = [3]int{1,2,3} 2. var num [3]int = [3]int {1,2,3} 3. num := [3 ...
- GoLang数组切片
1. 数组1.1 如何定义数组同java数组一样,数组是一组内存连续且类型相同的数据组成 //不初始化初始值默认为0 var arr1 = [5]int{} var arr2 = [5]int{1,2 ...
- [golang note] 数组切片
数组 √ golang数组包含的每个数据称为数组元素(element),数组包含的元素个数被称为数组长度(length). √ golang数组的长度在定义后不可更改,并且在声明时可以是一个常量或常量 ...
- golang中不定参数与数组切片的区别
package main import "fmt" func main() { myfunc1(, , , ) //传递不定数量的参数 myfunc2([], , , }) //传 ...
- Go语言学习之4 递归&闭包&数组切片&map&锁
主要内容: 1. 内置函数.递归函数.闭包2. 数组与切片3. map数据结构4. package介绍 5. 排序相关 1. 内置函数.递归函数.闭包 1)内置函数 (1). close:主要用来关闭 ...
- 窥探Swift之数组安全索引与数组切片
今天是元宵节,祝大家元宵节快乐!在Swift中的数组和字典中下标是非常常见的,数组可以通过索引下标进行元素的查询,字典可以通过键下标来获取相应的值.在使用数组时,一个常见的致命错误就是数组越界.如果在 ...
- 【SWIFT】从零开始的SWIFT语言学习笔记-2:简单值、数组与字典
1.0.3 简单值.数组与字典 知识点: 使用var创建变量 var myVariable = 65 myVariable = myVariable + 1 使用let创建常量 let myConst ...
- 窥探Swift之数组与字典
说到数组和字典,只要是编过程的小伙伴并不陌生.在Swift中的数组与字典也有着一些让人眼前一亮的特性,今天的博客就来窥探一下Swift中的Array和Dictionary.还是沿袭之前的风格,在介绍S ...
- Objective-C中把数组中字典中的数据转换成URL
可能上面的标题有些拗口,学过PHP的小伙伴们都知道,PHP中的数组的下标是允许我们自定义的,PHP中的数组确切的说就是键值对.而在OC我们要用字典(Dictionary)来存储,当然了Java用的是M ...
随机推荐
- 对于微信UnionID在公众平台以及小程序里面的获取
首先介绍下UnionID的作用,在注册了微信开放平台(注意,这里是开放平台,不是微信公众平台)之后,同一个微信号在这个开放平台下的项目上面的UnionID都是统一的,通俗的说就是,小程序跟公众号项目在 ...
- Spring.Net 依赖注入
一.Spring.Net概念 编程模型(Ioc,DI方式) IoC:控制反转 原来创建对象的权利由程序来控制就是new实例,IoC就是改由容器来创建,相当于一个工厂, DI:依赖注入 没有IoC就没有 ...
- spark源码阅读---Utils.getCallSite
1 作用 当该方法在spark内部代码中调用时,会返回当前调用spark代码的用户类的名称,以及其所调用的spark方法.所谓用户类,就是我们这些用户使用spark api的类. 2 内部实现 2.1 ...
- 开发规范 小白进阶 python代码规范化
开发规范 软件开发,规范项目的目录结构,代码规范,遵循 PeP8规范等等,让你更加清晰的,合理开发 一功能分类(文件名) settings.py配置文件 配置文件放一些静态参数, 划归固定的路径,文件 ...
- SpringBoot:实现定时任务
一.定时任务实现的几种方式: Timer 这是java自带的java.util.Timer类,这个类允许你调度一个java.util.TimerTask任务.使用这种方式可以让你的程序按照某一个频度执 ...
- xss实体绕过示例
知识点: 倘若是在script.input标签当中,即可突破. Payload ' oninput=alert`1` // 当要在input中输入内容时触发事件 ' oninput=alert`1` ...
- Linux内核DTB文件启动的几种方式
版权: 凌云物网智科实验室< www.iot-yun.com > 声明: 本文档由凌云物网智科实验室郭工编著! 作者: 郭文学< QQ: 281143292 guowen ...
- 0807 创建vue实例以及vue的基础指令
lession1 1.Vue的了解 渐进式框架 作者:尤雨溪 mvvm 2.创建vue实例 引入<script src="vue.js"><scr ...
- 2019-在iOS里添加admob横幅广告示例
下载sdk , 解压 导入项目文件夹: 在info.plist里加入应用id(不是广告单元id): GADApplicationIdentifier 设置Build Settings选项 设置ap ...
- Java连载22-for循环
一.循环结构 在程序当中总有一些需要反复的/重复的执行的代码,假设没有循环结构,那么这段需要重复执行的代码自然式子最需要重复编写的,代码无法得到重复使用,所以多数编程语言都是支持循环结构的,将来把需要 ...