Go数组、切片与map
数组
数组具有以下特点:
- 数组长度是固定的,不能动态变化。
- 数组中每个元素的类型和大小都相同。
- 数组中的元素在内存中是连续存储的,可以随机访问。
- Go中的数组是值类型,传递时复制整个数组。这意味着当它们被分配给一个新变量时,将把原始数组的副本分配给新变量。如果对新变量进行了更改,则不会在原始数组中反映。
- 如果要修改函数中的数组,必须使用指针。
- 比较两个数组(array)是否相等(使用 == 运算符)时,比较的是它们对应位置的值是否相等,而不是它们的地址或引用。
示例代码:
var a [4] float32 // 等价于:var arr2 = [4]float32{}
fmt.Println(a) // [0 0 0 0]
var b = [5] string{"ruby", "王二", "rose"}
fmt.Println(b) // [ruby 王二 rose ]
var c = [5] int{'A', 'B', 'C', 'D', 'E'} // byte
fmt.Println(c) // [65 66 67 68 69]
d := [...] int{1,2,3,4,5}// 根据元素的个数,设置数组的大小
fmt.Println(d)//[1 2 3 4 5]
e := [5] int{4: 100} // [0 0 0 0 100]
fmt.Println(e)
f := [...] int{0: 1, 4: 1, 9: 1} // [1 0 0 0 1 0 0 0 0 1]
fmt.Println(f)
切片
切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大。切片是一种方便、灵活且强大的包装器。
- 切片本身没有任何数据,它们只是对现有数组的引用。
- 切片可以理解为对数组的一个引用,它由指向数组的指针、切片的长度和切片的容量组成。切片的长度表示切片中实际存储的元素个数,而容量表示切片底层数组的大小。切片相比于数组的优势在于它的大小是动态可变的,可以根据需要进行扩容或缩减。
创建切片
var slice []int // 声明一个切片
slice := make([]int, 0) // 创建一个切片,并指定长度为0
var slice1 []type = make([]type, len)
// 也可以简写为
slice1 := make([]type, len)
s := arr[startIndex:endIndex]
// 将arr中从下标startIndex到endIndex-1 下的元素创建为一个新的切片(前闭后开),长度为endIndex-startIndex
s := arr[startIndex:]
s := arr[:endIndex]
修改切片
对切片进行修改非常方便,可以通过索引进行赋值或修改现有元素的值。此外,还可以使用切片作为参数传递给某个函数,并在函数内部对切片进行修改。这种特性使得切片在处理大量数据时非常高效。
- slice没有自己的任何数据。它只是底层数组的一个表示。对slice所做的任何修改都将反映在底层数组中。
- 当多个片共享相同的底层数组时,每个元素所做的更改将在数组中反映出来。
示例代码:
package main
import (
"fmt"
)
func main() {
numa := [3]int{78, 79 ,80}
nums1 := numa[:] //creates a slice which contains all elements of the array
nums2 := numa[:]
fmt.Println("array before change 1",numa) //[78 79 80]
nums1[0] = 100
fmt.Println("array after modification to slice nums1", numa) // [100 79 80]
nums2[1] = 101
fmt.Println("array after modification to slice nums2", numa) // [100 101 80]
}
空切片
一个切片在未初始化之前默认为 nil,长度为 0。
示例代码:
package main
import "fmt"
func main() {
var numbers []int
printSlice(numbers)
if(numbers == nil){
fmt.Printf("切片是空的")
}
}
func printSlice(x []int){
fmt.Printf("len=%d cap=%d slice=%v\n",len(x),cap(x),x)
}
输出:
len=0 cap=0 slice=[]
切片是空的
len()函数和cap()函数
Golang提供了两个内置函数len()和cap()来获取切片的长度和容量。len()函数返回切片中实际存储的元素个数,而cap()函数返回切片底层数组的大小。
- len()和cap()函数只能用于切片和数组,并且只能对第一维度的长度和容量进行计算。如果我们有一个切片的切片,那么len()函数将返回第一维度的长度,而不会返回第二维度的长度。
示例代码:
package main
import "fmt"
func main() {
var numbers = make([]int,3,5)
printSlice(numbers)
}
func printSlice(x []int){
fmt.Printf("len=%d cap=%d slice=%v\n",len(x),cap(x),x)
//len=3 cap=5 slice=[0 0 0]
}
append()函数
用于向切片中添加元素,可以一次添加一个或多个元素。
- append()函数接受两个参数:第一个参数是要添加元素的切片,第二个参数是要添加的元素。
- 添加完元素后,append()函数会返回一个新的切片,我们通常需要将其赋值给原始的切片。
- 除了添加元素,append()函数还有一个重要的功能——动态扩容。当切片的容量不足以容纳新添加的元素时,append()函数会自动进行扩容操作。它会创建一个新的底层数组,并将原始数据复制到新数组中。然后,它会将新添加的元素放入新数组中,并返回一个新的切片。这样,我们就可以使用扩容后的切片了。
- append函数会改变slice所引用的数组的内容,从而影响到引用同一数组的其它slice。 但当slice中没有剩余空间(即(cap-len) == 0)时,此时将动态分配新的数组空间。返回的slice数组指针将指向这个空间,而原数组的内容将保持不变;其它引用此数组的slice则不受影响。
copy() 函数
copy()函数用于将一个切片的内容复制到另一个切片中。
- copy()函数接受两个参数:第一个参数是目标切片,第二个参数是源切片。copy()函数会将源切片中的元素复制到目标切片中,并返回复制的元素个数。
- 与append()函数不同,copy()函数不会进行扩容操作。它仅将源切片中的元素按照顺序复制到目标切片中。因此,需要注意两个切片的大小要一致,否则可能会造成数据丢失或越界访问。
示例代码:
package main
import "fmt"
func main() {
/*
slice := arr[start:end]
切片中的数据:[start,end)
arr[:end],从头到end
arr[start:]从start到末尾
从已有的数组上,直接创建切片,该切片的底层数组就是当前的数组。
长度是从start到end切割的数据量。
但是容量从start到数组的末尾。
*/
a := [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
fmt.Println("----------1.已有数组直接创建切片--------------------")
s1 := a[:5] //1-5
s2 := a[3:8] //4-8
s3 := a[5:] // 6-10
s4 := a[:] // 1-10
fmt.Println("a:", a)
fmt.Println("s1:", s1)
fmt.Println("s2:", s2)
fmt.Println("s3:", s3)
fmt.Println("s4:", s4)
fmt.Printf("%p\n", &a)
fmt.Printf("%p\n", s1)
fmt.Println("----------2.长度和容量--------------------")
fmt.Printf("s1 len:%d,cap:%d\n", len(s1), cap(s1)) //s1,len:5,cap:10
fmt.Printf("s2 len:%d,cap:%d\n", len(s2), cap(s2)) //s2,len:5,cap:7
fmt.Printf("s3 len:%d,cap:%d\n", len(s3), cap(s3)) //s3,len:5,cap:5
fmt.Printf("s4 len:%d,cap:%d\n", len(s4), cap(s4)) //s4,len:10,cap:10
fmt.Println("----------3.更改数组的内容--------------------")
a[4] = 100
fmt.Println(a)
fmt.Println(s1)
fmt.Println(s2)
fmt.Println(s3)
fmt.Println("----------4.更改切片的内容--------------------")
s2[2] = 200
fmt.Println(a)
fmt.Println(s1)
fmt.Println(s2)
fmt.Println(s3)
fmt.Println("----------4.更改切片的内容--------------------")
s1 = append(s1, 1, 1, 1, 1)
fmt.Println(a)
fmt.Println(s1)
fmt.Println(s2)
fmt.Println(s3)
fmt.Println("----------5.添加元素切片扩容--------------------")
fmt.Println(len(s1), cap(s1))
s1 = append(s1, 2, 2, 2, 2, 2)
fmt.Println(a)
fmt.Println(s1)
fmt.Println(s2)
fmt.Println(s3)
fmt.Println(len(s1), cap(s1))
fmt.Printf("%p\n", s1)
fmt.Printf("%p\n", &a)
}
输出:
----------1.已有数组直接创建切片--------------------
a: [1 2 3 4 5 6 7 8 9 10]
s1: [1 2 3 4 5]
s2: [4 5 6 7 8]
s3: [6 7 8 9 10]
s4: [1 2 3 4 5 6 7 8 9 10]
0xc000018190
0xc000018190
----------2.长度和容量--------------------
s1 len:5,cap:10
s2 len:5,cap:7
s3 len:5,cap:5
s4 len:10,cap:10
----------3.更改数组的内容--------------------
[1 2 3 4 100 6 7 8 9 10]
[1 2 3 4 100]
[4 100 6 7 8]
[6 7 8 9 10]
----------4.更改切片的内容--------------------
[1 2 3 4 100 200 7 8 9 10]
[1 2 3 4 100]
[4 100 200 7 8]
[200 7 8 9 10]
----------4.更改切片的内容--------------------
[1 2 3 4 100 1 1 1 1 10]
[1 2 3 4 100 1 1 1 1]
[4 100 1 1 1]
[1 1 1 1 10]
----------5.添加元素切片扩容--------------------
9 10
[1 2 3 4 100 1 1 1 1 10]
[1 2 3 4 100 1 1 1 1 2 2 2 2 2]
[4 100 1 1 1]
[1 1 1 1 10]
14 20
0xc0000240a0
0xc000018190
map
map是无序的,每次打印出来的map都会不一样,它不能通过index获取,而必须通过key获取。
- map的长度是不固定的,也就是和slice一样,也是一种引用类型,当将映射分配给一个新变量时,它们都指向相同的内部数据结构。因此,一个的变化会反映另一个。
- 内置的len函数同样适用于map,返回map拥有的key的数量。
- map的key可以是所有可比较的类型,如布尔型、整数型、浮点型、复杂型、字符串型。
- 当key如果不存在的时候,我们会得到该value值类型的默认值,比如string类型得到空字符串,int类型得到0。但是程序不会报错。所以我们可以使用ok-idiom获取值,可知道key/value是否存在。
- map不能使用==操作符进行比较。
例一
// 例一
package main
import "fmt"
func main() {
//1.创建map
var map1 map[int]string //没有初始化,nil
var map2 = make(map[int]string) //map2 是通过 make 初始化的,即使它是空的,也不是 nil,因此 map2 == nil 返回 false
var map3 = map[string]int{"Go": 98, "Python": 87, "Java": 79, "Html": 93}
fmt.Println(map1)
fmt.Println(map2)
fmt.Println(map3)
fmt.Println(map1 == nil)
fmt.Println(map2 == nil)
fmt.Println(map3 == nil)
map2[1] = "11"
fmt.Println(map1[40]) //"" 是空的,但是不报错
v1, ok := map2[40]
if ok {
fmt.Println("对应的数值是:", v1)
} else {
fmt.Println("操作的key不存在,获取到的是零值:", v1)
}
}
输出:
map[]
map[]
map[Go:98 Html:93 Java:79 Python:87]
true
false
false
操作的key不存在,获取到的是零值:
例二
// 例二
package main
import "fmt"
func main() {
map4 := make(map[string]string)
map4["小王"] = "美"
map4["小李"] = "富"
map4["小文"] = "白"
fmt.Println(map4)
map5 := map4
fmt.Println(map5)
map5["小昭"] = "富"
fmt.Println(map4)
fmt.Println(map5)
}
输出:
map[小文:白 小李:富 小王:美]
map[小文:白 小李:富 小王:美]
map[小文:白 小昭:富 小李:富 小王:美]
map[小文:白 小昭:富 小李:富 小王:美]