golang数组

数组

概述

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
相关推荐
lamdaxu23 分钟前
分布式调用(02)
后端
daiyunchao24 分钟前
让Pomelo支持HTTP协议
后端
芒猿君1 小时前
AQS——同步器框架之源
后端
SaebaRyo1 小时前
手把手教你在网站中启用https和http2
后端·nginx·https
A-Kamen1 小时前
Spring Boot拦截器(Interceptor)与过滤器(Filter)深度解析:区别、实现与实战指南
java·spring boot·后端
豆豆酱1 小时前
Transformer结构详解
后端
upsilon1 小时前
golang切片slice
后端·go
狂奔小菜鸡1 小时前
Java运行时数据区
java·jvm·后端
lovebugs1 小时前
Java并发编程之Lock机制:更灵活的线程同步方案
后端·面试
kunge20131 小时前
Paddle快速入门
后端