目录
1 数组
1.1 简介
数组是具有相同唯一类型
的一组已编号且长度固定的数据项序列,这种类型可以是任意的原始类型例如整型、字符串或者自定义类型。
相对于去声明 number0, number1, ..., number99 的变量,使用数组形式 numbers[0], numbers[1] ..., numbers[99]
更加方便且易于扩展。
数组元素可以通过索引(位置)
来读取(或者修改),索引从 0
开始,第一个元素索引为 0,第二个索引为 1,以此类推。
1.1.1 声明数组
Go 语言数组声明需要指定元素类型及元素个数,语法格式如下:
var arrayName [size] dataType
其中,arrayName
是数组的名称,size
是数组的大小,dataType
是数组中元素的数据类型。
1.1.2 初始化数组
声明一个名为 numbers 的整数数组,其大小为 5,在声明时,数组中的每个元素都会根据其数据类型进行默认初始化,对于整数类型,初始值为 0。
var numbers [5]int
还可以使用初始化列表来初始化数组的元素:var numbers = [5]int{1, 2, 3, 4, 5}
,并将其中的元素分别初始化为 1、2、3、4 和 5。
另外,还可以使用 :=
简短声明语法来声明和初始化数组:numbers := [5]int{1, 2, 3, 4, 5}
注意
:在 Go 语言中,数组的大小是类型的一部分
,因此不同大小的数组是不兼容的,也就是说 [5]int
和 [10]int
是不同的类型。
如果数组长度不确定,可以使用 ...
代替数组的长度,编译器会根据元素个数自行推断数组的长度:var balance = [...]float32{1000.0, 2.0, 3.4, 7.0, 50.0}
或balance := [...]float32{1000.0, 2.0, 3.4, 7.0, 50.0}
如果设置了数组的长度,我们还可以通过指定下标
来初始化元素:
将索引为 1 和 3 的元素初始化
balance := [5]float32{1:2.0,3:7.0}
初始化数组中 {}
中的元素个数不能大于 []
中的数字。
1.3 访问数组元素
数组元素可以通过索引(位置
)来读取。格式为数组名后加中括号,中括号中为索引的值。例如:var salary float32 = balance[9]
以上实例读取了数组 balance 第 10 个元素的值。
以下演示了数组完整操作(声明、赋值、访问)的实例:
package main
import "fmt"
func main() {
var n [10]int /* n 是一个长度为 10 的数组 */
var i,j int
/* 为数组 n 初始化元素 */
for i = 0; i < 10; i++ {
n[i] = i + 100 /* 设置元素为 i + 100 */
}
/* 输出每个数组元素的值 */
for j = 0; j < 10; j++ {
fmt.Printf("Element[%d] = %d\n", j, n[j] )
}
}
实例 2
package main
import "fmt"
func main() {
var i,j,k int
// 声明数组的同时快速初始化数组
balance := [5]float32{1000.0, 2.0, 3.4, 7.0, 50.0}
/* 输出数组元素 */ ...
for i = 0; i < 5; i++ {
fmt.Printf("balance[%d] = %f\n", i, balance[i] )
}
balance2 := [...]float32{1000.0, 2.0, 3.4, 7.0, 50.0}
/* 输出每个数组元素的值 */
for j = 0; j < 5; j++ {
fmt.Printf("balance2[%d] = %f\n", j, balance2[j] )
}
// 将索引为 1 和 3 的元素初始化
balance3 := [5]float32{1:2.0,3:7.0}
for k = 0; k < 5; k++ {
fmt.Printf("balance3[%d] = %f\n", k, balance3[k] )
}
}
1.4 多维数组
Go 语言支持多维数组,以下为常用的多维数组声明方式:
var variable_name [SIZE1][SIZE2]...[SIZEN] variable_type
声明了三维的整型数组:var threedim [5][10][4]int
1.4.1 二维数组
二维数组是最简单的多维数组,二维数组本质上是由一维数组组成的。二维数组定义方式如下:
var arrayName [ x ][ y ] variable_type
二维数组中的元素可通过 a[ i ][ j ] 来访问。
package main
import "fmt"
func main() {
// Step 1: 创建数组
values := [][]int{}
// Step 2: 使用 append() 函数向空的二维数组添加两行一维数组
row1 := []int{1, 2, 3}
row2 := []int{4, 5, 6}
values = append(values, row1)
values = append(values, row2)
// Step 3: 显示两行数据
fmt.Println("Row 1")
fmt.Println(values[0])
fmt.Println("Row 2")
fmt.Println(values[1])
// Step 4: 访问第一个元素
fmt.Println("第一个元素为:")
fmt.Println(values[0][0])
}
1.4.2 初始化二维数组
多维数组可通过大括号
来初始值。以下实例为一个 3 行 4 列的二维数组:
a := [3][4]int{
{0, 1, 2, 3} , /* 第一行索引为 0 */
{4, 5, 6, 7} , /* 第二行索引为 1 */
{8, 9, 10, 11}, /* 第三行索引为 2 */
}
注意
:以上代码中倒数第二行的 }
必须要有逗号,因为最后一行的 }
不能单独一行
1.4.3 访问二维数组
二维数组通过指定坐标
来访问。如数组中的行索引与列索引,例如:
val := a[2][3]
或var value int = a[2][3]
以上实例访问了二维数组 val 第三行的第四个元素。
二维数组可以使用循环嵌套来输出元素:
package main
import "fmt"
func main() {
/* 数组 - 5 行 2 列*/
var a = [5][2]int{ {0,0}, {1,2}, {2,4}, {3,6},{4,8}}
var i, j int
/* 输出数组元素 */
for i = 0; i < 5; i++ {
for j = 0; j < 2; j++ {
fmt.Printf("a[%d][%d] = %d\n", i,j, a[i][j] )
}
}
}
以下实例创建各个维度元素数量不一致的多维数组:
package main
import "fmt"
func main() {
// 创建空的二维数组
animals := [][]string{}
// 创建三一维数组,各数组长度不同
row1 := []string{"fish", "shark", "eel"}
row2 := []string{"bird"}
row3 := []string{"lizard", "salamander"}
// 使用 append() 函数将一维数组添加到二维数组中
animals = append(animals, row1)
animals = append(animals, row2)
animals = append(animals, row3)
// 循环输出
for i := range animals {
fmt.Printf("Row: %v\n", i)
fmt.Println(animals[i])
}
}
1.5 数组与函数
Go 语言中的数组
是值类型
,因此在将数组传递给函数时,实际上是传递数组的副本
。如果想向函数传递数组参数,需要在函数定义时,声明形参为数组,可以通过以下两种方式来声明:
形参设定数组大小:
func myFunction(param [10]int) {
....
}
形参未设定数组大小:
func myFunction(param []int) {
....
}
示例
func main() {
/* 数组长度为 5 */
var balance = [5]int {1000, 2, 3, 17, 50}
var avg float32
/* 数组作为参数传递给函数 */
avg = getAverage( balance, 5 ) ;
/* 输出返回的平均值 */
fmt.Printf( "平均值为: %f ", avg );
}
func getAverage(arr [5]int, size int) float32 {
var i,sum int
var avg float32
for i = 0; i < size;i++ {
sum += arr[i]
}
avg = float32(sum) / float32(size)
return avg;
}
如果要在函数内修改原始数组,可以通过传递数组的指针来实现。
以下实例演示如何向函数传递数组,函数接受一个数组和数组的指针作为参数:
package main
import "fmt"
// 函数接受一个数组作为参数
func modifyArray(arr [5]int) {
for i := 0; i < len(arr); i++ {
arr[i] = arr[i] * 2
}
}
// 函数接受一个数组的指针作为参数
func modifyArrayWithPointer(arr *[5]int) {
for i := 0; i < len(*arr); i++ {
(*arr)[i] = (*arr)[i] * 2
}
}
func main() {
// 创建一个包含5个元素的整数数组
myArray := [5]int{1, 2, 3, 4, 5}
fmt.Println("Original Array:", myArray)
// 传递数组给函数,但不会修改原始数组的值
modifyArray(myArray)
fmt.Println("Array after modifyArray:", myArray)
// 传递数组的指针给函数,可以修改原始数组的值
modifyArrayWithPointer(&myArray)
fmt.Println("Array after modifyArrayWithPointer:", myArray)
}
2 切片
2.1 简介
切片是对数组的抽象,数组的长度不可改变,在特定场景中这样的集合就不太适用,Go 中提供了一种灵活,功能强悍的内置类型切片(动态数组
),与数组相比切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大。
2.1.1 定义切片
可以声明一个未指定大小的数组来定义切片:var identifier []type
,切片不需要说明长度。
或使用 make()
函数来创建切片:var slice1 []type = make([]type, len)
,也可以简写为:slice1 := make([]type, len)
也可以指定容量,其中 capacity
为可选参数:make([]T, length, capacity)
这里 len 是数组的长度并且也是切片的初始长度。
2.1.2 切片初始化
切片可以通过数组来初始化,也可以通过内置函数 make()
初始化,初始化时 len=cap
,在追加元素时如果容量 cap 不足时将按 len 的 2 倍扩容。
s :=[] int {1,2,3 }
:直接初始化切片,[]
表示是切片类型,{1,2,3}
初始化值依次是 1,2,3,其 cap=len=3。s := arr[:]
:初始化切片 s,是数组arr
的引用。s := arr[startIndex:endIndex]
:将 arr 中从下标startIndex
到endIndex-1
下的元素创建为一个新的切片。s := arr[startIndex:]
:默认 endIndex 时将表示一直到arr的最后一个元素。s := arr[:endIndex]
:默认 startIndex 时将表示从 arr 的第一个元素开始。s1 := s[startIndex:endIndex]
:通过切片 s 初始化切片 s1。s :=make([]int,len,cap)
:通过内置函数make()
初始化切片s,[]int
标识为其元素类型为 int 的切片。
2.1.3 len() 和 cap() 函数
切片是可索引的,并且可以由 len()
方法获取长度。切片提供了计算容量的方法 cap() 可以测量切片最长可以达到多少。
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)
}
2.1.4 空(nil)切片
一个切片在未初始化之前默认为 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=[]
切片是空的
2.2 切片操作
2.2.1 切片截取
可以通过设置下限及上限来设置截取切片 [lower-bound:upper-bound]
,实例如下:
package main
import "fmt"
func main() {
/* 创建切片 */
numbers := []int{0,1,2,3,4,5,6,7,8}
printSlice(numbers)
/* 打印原始切片 */
fmt.Println("numbers ==", numbers)
/* 打印子切片从索引1(包含) 到索引4(不包含)*/
fmt.Println("numbers[1:4] ==", numbers[1:4])
/* 默认下限为 0*/
fmt.Println("numbers[:3] ==", numbers[:3])
/* 默认上限为 len(s)*/
fmt.Println("numbers[4:] ==", numbers[4:])
numbers1 := make([]int,0,5)
printSlice(numbers1)
/* 打印子切片从索引 0(包含) 到索引 2(不包含) */
number2 := numbers[:2]
printSlice(number2)
/* 打印子切片从索引 2(包含) 到索引 5(不包含) */
number3 := numbers[2:5]
printSlice(number3)
}
func printSlice(x []int){
fmt.Printf("len=%d cap=%d slice=%v\n",len(x),cap(x),x)
}
2.2.2 append() 和 copy()
如果想增加切片的容量,我们必须创建一个新的更大的切片并把原分片的内容都拷贝过来。
append
和 copy
是生成了个新的切片,对原切片没影响
下面的代码描述了从拷贝切片的 copy 方法和向切片追加新元素的 append 方法。
需要注意一下:append操作会创建新切片
package main
import "fmt"
func main() {
var numbers []int
printSlice(numbers)
/* 允许追加空切片 */
numbers = append(numbers, 0)
printSlice(numbers)
/* 向切片添加一个元素 */
numbers = append(numbers, 1)
printSlice(numbers)
/* 同时添加多个元素 */
numbers = append(numbers, 2,3,4)
printSlice(numbers)
/* 创建切片 numbers1 是之前切片的两倍容量*/
numbers1 := make([]int, len(numbers), (cap(numbers))*2)
/* 拷贝 numbers 的内容到 numbers1 */
copy(numbers1,numbers)
printSlice(numbers1)
}
func printSlice(x []int){
fmt.Printf("len=%d cap=%d slice=%v\n",len(x),cap(x),x)
}
2.2.3 切片展开 ...
可以使用...
操作符将一个切片追加到另一个切片。
package main
import (
"fmt"
)
func main() {
veggies := []string{"potatoes", "tomatoes", "brinjal"}
fruits := []string{"oranges", "apples"}
food := append(veggies, fruits...)
fmt.Println("food:", food)
}
上面的程序中,将 fruits 追加到 veggies 并赋值给 food。...
操作符用来展开切片。程序的输出为:food: [potatoes tomatoes brinjal oranges apples]。
注意
:切片本身作为引用类型
传递,即使展开它
,底层数组还是共享的。所以即使切片被展开
成多个单独的字符串参数,如果有函数内的修改仍然会影响
原始切片
2.3 切片与函数
在做函数调用时,slice
按引用传递,array
按值传递:
package main
import "fmt"
func main(){
changeSliceTest()
}
func changeSliceTest() {
arr1 := []int{1, 2, 3}
arr2 := [3]int{1, 2, 3}
arr3 := [3]int{1, 2, 3}
fmt.Println("before change arr1, ", arr1)
changeSlice(arr1) // slice 按引用传递
fmt.Println("after change arr1, ", arr1)
fmt.Println("before change arr2, ", arr2)
changeArray(arr2) // array 按值传递
fmt.Println("after change arr2, ", arr2)
fmt.Println("before change arr3, ", arr3)
changeArrayByPointer(&arr3) // 可以显式取array的 指针
fmt.Println("after change arr3, ", arr3)
}
func changeSlice(arr []int) {
arr[0] = 9999
}
func changeArray(arr [3]int) {
arr[0] = 6666
}
func changeArrayByPointer(arr *[3]int) {
arr[0] = 6666
}
结果:
before change arr1, [1 2 3]
after change arr1, [9999 2 3]
before change arr2, [1 2 3]
after change arr2, [1 2 3]
before change arr3, [1 2 3]
after change arr3, [6666 2 3]
2.4 内存优化
切片
持有对底层数组
的引用。只要切片在内存中,就不能对数组进行垃圾回收。在内存管理方面,这是必须要关注的。假设我们有一个非常大的数组,我们只处理它的一小部分。因此,我们从该数组创建一个切片并开始处理切片。这里需要注意的重要一点是,由于切片引用了数组,因此数组仍然在内存中。
解决该问题的一个方法是使用 copy
函数 func copy(dst, src []T) int
来创建该切片的一个拷贝。这样我们就可以使用这个新的切片,原来的数组可以被垃圾回收。
package main
import (
"fmt"
)
func countries() []string {
countries := []string{"USA", "Singapore", "Germany", "India", "Australia"}
neededCountries := countries[:len(countries)-2]
countriesCpy := make([]string, len(neededCountries))
copy(countriesCpy, neededCountries) //copies neededCountries to countriesCpy
return countriesCpy
}
func main() {
countriesNeeded := countries()
fmt.Println(countriesNeeded)
}
在上面程序中,neededCountries := countries[:len(countries)-2] 创建一个底层数组为 countries 并排除最后两个元素的切片。
copy 将 neededCountries 拷贝到 countriesCpy 并在下一行返回 countriesCpy。现在数组 countries 可以被垃圾回收,因为 neededCountries 不再被引用。
3 Map
3.1 简介
Map
是一种无序的键值对的集合,Map 最重要的一点是通过 key
来快速检索数据,key
类似于索引
,指向数据的值。
Map
是一种集合,所以我们可以像迭代数组和切片那样迭代它。不过,Map 是无序的,遍历 Map 时返回的键值对的顺序是不确定的。
在获取 Map 的值时,如果键不存在,返回该类型的零值,例如 int 类型的零值是 0,string 类型的零值是 ""。
Map
是引用类型
,如果将一个 Map
传递给一个函数或赋值给另一个变量,它们都指向同一个底层数据结构,因此对 Map
的修改会影响到所有引用它的变量。
3.2 定义 Map
可以使用内建函数 make
或使用 map
关键字来定义 Map
/* 使用 make 函数 */
map_variable := make(map[KeyType]ValueType, initialCapacity)
其中 KeyType
是键的类型,ValueType
是值的类型,initialCapacity
是可选的参数,用于指定 Map
的初始容量。Map
的容量是指 Map
中可以保存的键值对的数量,当 Map 中的键值对数量达到容量时,Map
会自动扩容。如果不指定 initialCapacity
,Go 语言会根据实际情况选择一个合适的值。
// 创建一个空的 Map
m := make(map[string]int)
// 创建一个初始容量为 10 的 Map
m := make(map[string]int, 10)
也可以使用字面量创建 Map:
// 使用字面量创建 Map
m := map[string]int{
"apple": 1,
"banana": 2,
"orange": 3,
}
映射的键可以是任何值,这个值的类型并不限制,内置类型或者结构体都可以,需要确定这个值可以使用运算符做比较。需要注意的是,切片、函数以及包含切片的结构类型由于是引用类型,均不能作为映射的键。
dict := map[[]string]int{
} // 报错:incomparable map key type []string
dict := map[int][]string{
} // 切片可以作为值,不可以作为键
3.3 操作Map
获取元素:
// 获取键值对
v1 := m["apple"]
v2, ok := m["pear"] // 如果键不存在,ok 的值为 false,v2 的值为该类型的零值
修改元素:
// 修改键值对
m["apple"] = 5
获取 Map 的长度:
// 获取 Map 的长度
len := len(m)
遍历 Map:
// 遍历 Map
for k, v := range m {
fmt.Printf("key=%s, value=%d\n", k, v)
}
删除元素:
// 删除键值对
delete(m, "banana")
3.4 示例
package main
import "fmt"
func main() {
var siteMap map[string]string /*创建集合 */
siteMap = make(map[string]string)
/* map 插入 key - value 对,各个国家对应的首都 */
siteMap [ "Google" ] = "谷歌"
siteMap [ "Runoob" ] = "菜鸟教程"
siteMap [ "Baidu" ] = "百度"
siteMap [ "Wiki" ] = "维基百科"
/*使用键输出地图值 */
for site := range siteMap {
fmt.Println(site, "首都是", siteMap [site])
}
/*查看元素在集合中是否存在 */
name, ok := siteMap [ "Facebook" ] /*如果确定是真实的,则存在,否则不存在 */
/*fmt.Println(capital) */
/*fmt.Println(ok) */
if (ok) {
fmt.Println("Facebook 的 站点是", name)
} else {
fmt.Println("Facebook 站点不存在")
}
}
标签:Map,func,int,fmt,切片,numbers,数组,Go
From: https://www.cnblogs.com/jingzh/p/18638381