数组
概述
Golang的数组不是可变数组,而是严格遵循了数据结构的定义。所以他具有以下特点:
数据结构中数组的定义:
sh
数组(Array)是一种线性表数据结构。它用一组连续的内存空间,来存储一组具有相同类型的数据。
所以golang中数组具有如下性质:
sh
1.数组储存同类型的元素。
"原因":数组要开辟连续且明确的空间,数据类型一样就可以通过元素个数*数据大小得到要开辟的空间大小。
2.数组的长度一旦确定就不可改变。
"原因":数组开辟的连续空间是不会增加或减少的,通过最初指定的长度计算出空间大小后就会确定下来。
3.数组一旦确定就不可改变,数组的确定需要满足以下两点:
"元素类型":用于计算每项的偏移量
"长度":偏移的次数
至于偏移的开始位置,golang会自行决定。
由于数组定义后不可变的特性,golang将数组设定为值类型。
数组的初始化
声明数组
基本语法
按照数组的定义很容易得知数组的声明格式。
go
var 数组变量名 [数组长度]元素类型;
举例
go
package array_knowledge
import "fmt"
func CreateArr(){
//声明数组
var arr1 [5]string;
fmt.Printf("arr1的值为%#v\n",arr1)
}
调用
go
package main
import (
"go_learn/array_knowledge"
)
func main(){
//声明数组
array_knowledge.CreateArr()
}
结果
sh
arr1的值为[5]string{"", "", "", "", ""}
数组声明了却没有赋值会用类型的零值填充元素。
声明数组并赋值
基本语法
go
//完整赋值格式 注意赋值类型和赋值个数
var 数组变量名 [数组长度]元素类型 = [数组长度]元素类型{元素1,元素2,...}
由于赋值时值已经表明类型和元素个数,所以左边不用显式指明类型。
go
var 数组变量名 = [数组长度]元素类型{元素1,元素2,...}
简易声明并赋值
go
数组变量名 := [数组长度]元素类型{元素1,元素2,...}
举例
go
package array_knowledge
import "fmt"
func CreateArr(){
//声明并赋值--完整版
var arr2 [3]int64 = [3]int64{1,2,3}
//声明并赋值--左边类型自推导
var arr3 =[4]bool{true,false,true,false}
//简易声明并赋值
arr4 := [2]string{"hello","world"}
fmt.Printf("arr2的值为%#v\n",arr2)
fmt.Printf("arr3的值为%#v\n",arr3)
fmt.Printf("arr4的值为%#v\n",arr4)
}
调用
go
package main
import (
"go_learn/array_knowledge"
)
func main(){
//声明数组
array_knowledge.CreateArr()
}
结果
go
arr2的值为[3]int64{1, 2, 3}
arr3的值为[4]bool{true, false, true, false}
arr4的值为[2]string{"hello", "world"}
注意事项
sh
1.数组赋值的元素个数不能超过数组长度。(数组的内存空间固定,多了放不下会报错)
2.数组赋值的元素类型必须是数组类型指定元素。
数组赋值
赋值元素数量不够
当我们赋值的元素个数少于数组长度时将按照位置一一赋值,后续的用零值填充。
举例
go
package array_knowledge
import "fmt"
func CreateArrVal(){
//赋值元素个数少于数组长度
var arr1 = [4]int{1,2}
fmt.Printf("arr1的值为%#v\n",arr1)
}
调用
go
package main
import (
"go_learn/array_knowledge"
)
func main(){
//声明数组
array_knowledge.CreateArr()
}
结果
sh
arr1的值为[4]int{1, 2, 0, 0}
总结
sh
1.赋值元素个数不能超过数组长度。
2.赋值元素个数少于数组长度时后续元素将用零值填充。
使用下标赋值
等价于直接给下标赋值,语法
go
var 数组变量 = [数组长度]类型{下标A:值1,下标B:值2}
//等价于
var 数组变量 [数组长度]类型;
数组[下标A] = 值1
数组[下标B] = 值2
由此我们可以知道,只要不越界就是合法的。
举例
go
func CreateArrVal(){
//使用下标赋值
var arr2 = [5]string{1:"world",4:"happy"}
fmt.Printf("arr2的值为%#v\n",arr2)
}
结果
go
arr2的值为[5]string{"", "world", "", "", "happy"}
下标与位置混用
不推荐这种写法:这里只是做了解。
规则
1.第一个出现的位置元素,如果前面没有下标元素,则默认从0开始,此时再声明下标0的则为非法。
go
//"sad"元素左边没有下标元素,所以从0开始
var arr3 = [5]string{"sad",2:"world","hello",4:"happy"}
//报错:"sad"已经占据了下标0,下标0元素赋值失败
var arr3 = [5]string{"sad",0:"haha",2:"world","hello",4:"happy"}
2.如果位置元素左边有下标元素,则后续出现的位置元素,从下标元素处计算
go
//"hello"出现在1:"world"后,所以为2:"hello"
var arr3 = [5]string{0:"haha",1:"world","hello",4:"happy"}
//等价于
var arr3 = [5]string{0:"haha",1:"world",2:"hello",4:"happy"}
3.注意下标重复问题
go
var arr3 = [5]string{0:"haha",2:"world","hello","sad",4:"happy"}
//下标自动往后推,所以等价于
//报错:下标4重复了
var arr3 = [5]string{0:"haha",2:"world",3:"hello",4:"sad",4:"happy"}
4.注意乱序问题,乱序后的位置元素计算逻辑仍然是相对最近的计算。
go
var arr3 = [5]string{2:"haha",0:"world","hello",4:"happy"}
//等价于
var arr3 = [5]string{0:"world",1:"hello",2:"haha",4:"happy"}
总结
sh
1.位置元素的下标会根据左边最近的下标元素推导,如果没有就从0开始
2.得到最后的全含下标的数组元素,如果出现下标重复或者下标越界就报错。
不定长数组
有时我们可能无法确定数组具体长度,此时可以通过第一次赋值得到数组长度。
语法
go
var 数组变量 = [...]类型{元素1,元素2,...}
这种方式声明数组必须同时赋值,通过赋值来确定最终的数组个数,例如
go
var arr2 = [...]int{1,2,3,4}
//最终等价于
var arr2 = [4]int{1,2,3,4}
我们也可以采用下标赋值
go
var arr5 =[...]string{5:"a",10:"b"}
//最终将采用最大的下标来得到数组,数组长度=最大下标+1
//注意下标不能重复,且不能出现上述混用的错误
var arr5 = [11]string{5:"a",10:"b"}
举例
go
package array_knowledge
import "fmt"
func CreateArrVal(){
//不定长数组
var arr4 = [...]int64{1,2,3,4}
fmt.Printf("arr4的值为%#v\n",arr4)
//用下标设置不定长数组
var arr5 =[...]string{5:"a",10:"b"}
fmt.Printf("arr5的值为%#v\n",arr5)
}
调用
go
package main
import (
"go_learn/array_knowledge"
)
func main(){
//声明数组
array_knowledge.CreateArrVal()
}
结果
sh
arr4的值为[4]int64{1, 2, 3, 4}
arr5的值为[11]string{"", "", "", "", "", "a", "", "", "", "", "b"}
数组的访问与修改
数组可以通过下标来访问并修改元素。
go
//访问元素
数组变量[下标];
//如果该下标原来没有赋值就是零值
//该操作属于修改数组的值
数组变量[下标]= 值;
举例
go
package array_knowledge
import "fmt"
func GetArrVal(){
var arr_new = [3]string{"a","b","c"}
arr_new[2] = "d"
fmt.Printf("arr_new的值为%#v\n",arr_new)
}
结果
go
arr_new的值为[3]string{"a", "b", "d"}
数组类型
数组变量的最终类型是
go
[数组长度]类型
//例如
[3]string
[2]map[string]int
注意事项
数组类型是由数组长度和元素类型组成的,二者要都相同才属于同一数组类型,否则就是不同数组类型。
即使是不定长数组再最终也会根据赋值计算出长度
go
//属于同一数组类型
var a [2]int64
var b [2]int64
//属于不同数组类型
var c [2]int64
var d [2]int32 //元素类型不同
var e [3]int64 //元素长度不同
数组作为参数
这里要与js和python进行区分,golang
中数组是值类型,所以直接作为参数传递不会影响到外部原始数组。
举例
go
package array_knowledge
import "fmt"
func ChangeArr(arr [3]string){
arr[2] = "gender"
fmt.Printf("函数内部的数组为%#v\n",arr)
}
调用
go
package main
import (
"fmt"
"go_learn/array_knowledge"
)
//这是入口文件
func main(){
//测试数组是值类型
var arrA = [3]string{"name","age","sex"}
array_knowledge.ChangeArr(arrA)
fmt.Printf("函数外部的数组为%#v\n",arrA)
}
结果
go
函数内部的数组为[3]string{"name", "age", "gender"}
函数外部的数组为[3]string{"name", "age", "sex"}
结果证明数组是值类型,函数内修改数组不会影响到原始数组。
使用指针类型传递参数
要让函数内能修改值类型的原始变量,我们需要借助指针类型。
注意
go
//这是数组的指针类型
*[长度]类型
//这是存储指针类型元素的数组
[长度]*类型
举例
go
package array_knowledge
import "fmt"
func ChangeArrByPtr(arrPtr *[3]string){
//数组与一般值类型的区别
/*
一般值类型的修改是 *指针变量 = 新值
但是数组是直接 指针类型变量 = 新值
省略了取内容的过程,不仅如此,对数组使用`*指针类型变量`来修改是错误的
*/
arrPtr[2] = "gender"
//修改的时候使用指针变量,但是访问值还是要取*
fmt.Printf("函数内部的数组为%#v\n",*arrPtr)
}
调用
go
package main
import (
"fmt"
"go_learn/array_knowledge"
)
//这是入口文件
func main(){
//通过指针修改数组
var arrB = [3]string{"name","age","sex"}
array_knowledge.ChangeArrByPtr(&arrB)
fmt.Printf("函数外部的数组为%#v\n",arrB)
}
结果
sh
函数内部的数组为[3]string{"name", "age", "gender"}
函数外部的数组为[3]string{"name", "age", "gender"}
注意
sh
一般值类型的修改是 *指针变量 = 新值
但是数组是直接 指针类型变量 = 新值
数组省略了取内容的过程,不仅如此,对数组使用`*指针类型变量`来修改是错误的
例如
go
//使用值类型的指针传参实现对原始变量的修改
func ChangeByPtr(a *int64){
//通过指针变量来获取原始值,*是取内容
*a += 5
}
这一点对结构体也适用,所以在值类型中用指针类型变量操作数组和结构体
与其他值类型是不一样的。
个人感觉这个golang的设计者故意留下的区别。
理解
sh
1.用指针类型变量来`操作`结构体和数组时,直接使用指针类型变量。
2.但是要访问最终值仍然需要`*指针类型变量`
数组的长度和容量
数组的长度和容量是一样的,语法
go
//获取数组长度
len(数组)
//获取数组容量
cap(数组)
这两个都是golang内置方法,容量对于以后判断是否为同一类切片具有极其关键的作用。
举例
go
package array_knowledge
import "fmt"
func CapandLen(){
var arr [5]int64;
fmt.Printf("数组arr的容量为%v\n",cap(arr))
fmt.Printf("数组arr的长度为%v\n",len(arr))
}
调用
go
package main
import (
"fmt"
"go_learn/array_knowledge"
)
func main(){
//获取数组长度和容量
array_knowledge.CapandLen()
}
结果
sh
数组arr的容量为5
数组arr的长度为5
遍历数组
我们可以用for循环或者for...range遍历数组.
举例
go
package array_knowledge
import "fmt"
func GetEveryItem(){
arr1 := [3]string{"I","am","one"}
//用for循环遍历数组
for i:=0;i<len(arr1);i++{
fmt.Printf("for:第%d个数组元素为%s\n",i+1,arr1[i])
}
//用for...range遍历数组
for idx,val := range arr1{
fmt.Printf("range:第%d个数组元素为%s\n",idx+1,val)
}
}
调用
go
package main
import (
"fmt"
"go_learn/array_knowledge"
)
func main(){
array_knowledge.GetEveryItem()
}
结果
go
for:第1个数组元素为I
for:第2个数组元素为am
for:第3个数组元素为one
range:第1个数组元素为I
range:第2个数组元素为am
range:第3个数组元素为one