博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Go学习(4):数组和切片
阅读量:4059 次
发布时间:2019-05-25

本文共 9226 字,大约阅读时间需要 30 分钟。

一、数组(Array)

1.1 什么是数组

Go 语言提供了数组类型的数据结构。

数组是具有相同唯一类型的一组已编号且长度固定的数据项序列,这种类型可以是任意的原始类型例如整形、字符串或者自定义类型。

数组元素可以通过索引(位置)来读取(或者修改),索引从0开始,第一个元素索引为 0,第二个索引为 1,以此类推。数组的下标取值范围是从0开始,到长度减1。

数组一旦定义后,大小不能更改。这些性质和java中的数组类似.

1.2 数组的语法

声明和初始化数组

需要指明数组的大小和存储的数据类型。

var variable_name [SIZE] variable_type

示例代码:

var balance [10] float32var balance = [5]float32{
1000.0, 2.0, 3.4, 7.0, 50.0}

初始化数组中 {} 中的元素个数不能大于 [] 中的数字。

如果忽略 [] 中的数字不设置数组大小,Go 语言会根据元素的个数来设置数组的大小:

var balance = []float32{
1000.0, 2.0, 3.4, 7.0, 50.0}
balance[4] = 50.0

数组的其他创建方式:

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)

备注 :

Println :可以打印出字符串,和变量
Printf : 只可以打印出格式化的字符串,可以输出字符串类型的变量,不可以输出整形变量和整形
当需要格式化输出信息时一般选择 Printf,其他时候用 Println 就可以了

访问数组元素

float32 salary = balance[9]

示例代码:

package mainimport "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] ) }}

运行结果:

Element[0] = 100Element[1] = 101Element[2] = 102Element[3] = 103Element[4] = 104Element[5] = 105Element[6] = 106Element[7] = 107Element[8] = 108Element[9] = 109

数组的长度

通过将数组作为参数传递给len函数,可以获得数组的长度。

示例代码:

package mainimport "fmt"func main() {
a := [...]float64{
67.7, 89.8, 21, 78} fmt.Println("length of a is",len(a))}

运行结果:

length of a is 4

您甚至可以忽略声明中数组的长度并将其替换为…让编译器为你找到长度。这是在下面的程序中完成的。

示例代码:

package mainimport (      "fmt")func main() {
a := [...]int{
12, 78, 50} // ... makes the compiler determine the length fmt.Println(a)}

遍历数组:

package mainimport "fmt"func main() {
a := [...]float64{
67.7, 89.8, 21, 78} for i := 0; i < len(a); i++ {
//looping from 0 to the length of the array fmt.Printf("%d th element of a is %.2f\n", i, a[i]) }}

使用range遍历数组:

package mainimport "fmt"func main() {
a := [...]float64{
67.7, 89.8, 21, 78} sum := float64(0) for i, v := range a {
//range returns both the index and value fmt.Printf("%d the element of a is %.2f\n", i, v) sum += v } fmt.Println("\nsum of all elements of a",sum)}

如果您只需要值并希望忽略索引,那么可以通过使用_ blank标识符替换索引来实现这一点。

for _, v := range a {
//ignores index }

1.3 多维数组

Go 语言支持多维数组,以下为常用的多维数组声明语法方式:

var variable_name [SIZE1][SIZE2]...[SIZEN] variable_type
var threedim [5][10][4]int

二维数组

多维数组可通过大括号来初始值。以下实例为一个 3 行 4 列的二维数组:

a = [3][4]int{
{
0, 1, 2, 3} , /* 第一行索引为 0 */ {
4, 5, 6, 7} , /* 第二行索引为 1 */ {
8, 9, 10, 11} /* 第三行索引为 2 */}
int val = a[2][3]

以上实例访问了二维数组 val 第三行的第四个元素。

遍历二维数组:

package mainimport "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] ) } }}

1.4 数组是值类型

数组是值类型

Go中的数组是值类型,而不是引用类型。这意味着当它们被分配给一个新变量时,将把原始数组的副本分配给新变量。如果对新变量进行了更改,则不会在原始数组中反映。

package mainimport "fmt"func main() {
a := [...]string{
"USA", "China", "India", "Germany", "France"} b := a // a copy of a is assigned to b b[0] = "Singapore" fmt.Println("a is ", a) fmt.Println("b is ", b) }

运行结果:

a is [USA China India Germany France]  b is [Singapore China India Germany France]

类似地,当将数组传递给函数作为参数时,它们将通过值传递,而原始数组将保持不变。

向函数传递数组

第一种

void myFunction(param [10]int){
...}

第二种

void myFunction(param []int){
...}
func getAverage(arr []int, int size) float32{
var i int var avg, sum float32 for i = 0; i < size; ++i {
sum += arr[i] } avg = sum / size return avg;}
package mainimport "fmt"func changeLocal(num [5]int) {
num[0] = 55 fmt.Println("inside function ", num)}func main() {
num := [...]int{
5, 6, 7, 8, 8} fmt.Println("before passing to function ", num) changeLocal(num) //num is passed by value fmt.Println("after passing to function ", num)}

运行结果:

before passing to function  [5 6 7 8 8]  inside function  [55 6 7 8 8]  after passing to function  [5 6 7 8 8]

数组的大小是类型的一部分。因此[5]int和[25]int是不同的类型。因此,数组不能被调整大小。不要担心这个限制,因为切片的存在是为了解决这个问题。

package mainfunc main() {
a := [3]int{
5, 78, 8} var b [5]int b = a //not possible since [3]int and [5]int are distinct types}

二、切片(Slice)

2.1 什么是切片

Go 语言切片是对数组的抽象

Go 数组的长度不可改变,在特定场景中这样的集合就不太适用,Go中提供了一种灵活,功能强悍的内置类型切片(“动态数组”),与数组相比切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大

切片是一种方便、灵活且强大的包装器。切片本身没有任何数据。它们只是对现有数组的引用。

切片与数组相比,不需要设定长度,在[]中不用设定值,相对来说比较自由

从概念上面来说slice像一个结构体,这个结构体包含了三个元素:

  1. 指针,指向数组中slice指定的开始位置
  2. 长度,即slice的长度
  3. 最大长度,也就是slice开始位置到数组的最后位置的长度

2.2 切片的语法

定义切片

var identifier []type

切片不需要说明长度。

或使用make()函数来创建切片:

var slice1 []type = make([]type, len)也可以简写为slice1 := make([]type, len)
make([]T, length, capacity)

初始化

s[0] = 1s[1] = 2s[2] = 3
s :=[] int {
1,2,3 }
s := arr[startIndex:endIndex]

将arr中从下标startIndex到endIndex-1 下的元素创建为一个新的切片(前闭后开),长度为endIndex-startIndex

s := arr[startIndex:]

缺省endIndex时将表示一直到arr的最后一个元素

s := arr[:endIndex]

缺省startIndex时将表示从arr的第一个元素开始

package mainimport (      "fmt")func main() {
a := [5]int{
76, 77, 78, 79, 80} var b []int = a[1:4] //creates a slice from a[1] to a[3] fmt.Println(b)}

2.3 修改切片

slice没有自己的任何数据。它只是底层数组的一个表示。对slice所做的任何修改都将反映在底层数组中。

示例代码:

package mainimport (      "fmt")func main() {
darr := [...]int{
57, 89, 90, 82, 100, 78, 67, 69, 59} dslice := darr[2:5] fmt.Println("array before",darr) for i := range dslice {
dslice[i]++ } fmt.Println("array after",darr) }

运行结果:

array before [57 89 90 82 100 78 67 69 59]  array after [57 89 91 83 101 78 67 69 59]

当多个片共享相同的底层数组时,每个元素所做的更改将在数组中反映出来。

示例代码:

package mainimport (      "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) nums1[0] = 100 fmt.Println("array after modification to slice nums1", numa) nums2[1] = 101 fmt.Println("array after modification to slice nums2", numa)}

运行结果:

array before change 1 [78 79 80]  array after modification to slice nums1 [100 79 80]  array after modification to slice nums2 [100 101 80]

2.4 len() 和 cap() 函数

切片的长度是切片中元素的数量。切片的容量是从创建切片的索引开始的底层数组中元素的数量。

切片是可索引的,并且可以由 len() 方法获取长度

切片提供了计算容量的方法 cap() 可以测量切片最长可以达到多少

package mainimport "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]

空切片

一个切片在未初始化之前默认为 nil,长度为 0

package mainimport "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=[]切片是空的
package mainimport "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)}

结果

len=9 cap=9 slice=[0 1 2 3 4 5 6 7 8]numbers == [0 1 2 3 4 5 6 7 8]numbers[1:4] == [1 2 3]numbers[:3] == [0 1 2]numbers[4:] == [4 5 6 7 8]len=0 cap=5 slice=[]len=2 cap=9 slice=[0 1]len=3 cap=7 slice=[2 3 4]

2.5 append() 和 copy() 函数

append 向slice里面追加一个或者多个元素,然后返回一个和slice一样类型的slice

copy 函数copy从源slice的src中复制元素到目标dst,并且返回复制的元素的个数

append函数会改变slice所引用的数组的内容,从而影响到引用同一数组的其它slice。 但当slice中没有剩

余空间(即(cap-len) == 0)时,此时将动态分配新的数组空间。返回的slice数组指针将指向这个空间,而原
数组的内容将保持不变;其它引用此数组的slice则不受影响

下面的代码描述了从拷贝切片的 copy 方法和向切片追加新元素的 append 方法

package mainimport "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)}

结果

len=0 cap=0 slice=[]len=1 cap=2 slice=[0]len=2 cap=2 slice=[0 1]len=5 cap=8 slice=[0 1 2 3 4]len=5 cap=12 slice=[0 1 2 3 4]

numbers1与numbers两者不存在联系,numbers发生变化时,numbers1是不会随着变化的。也就是说copy方法是不会建立两个切片的联系的


整理自互联网

转载地址:http://wlwji.baihongyu.com/

你可能感兴趣的文章
JS牛客网编译环境的使用
查看>>
9、VUE面经
查看>>
关于进制转换的具体实现代码
查看>>
Golang 数据可视化利器 go-echarts ,实际使用
查看>>
mysql 跨机器查询,使用dblink
查看>>
mysql5.6.34 升级到mysql5.7.32
查看>>
dba 常用查询
查看>>
Oracle 异机恢复
查看>>
Oracle 12C DG 搭建(RAC-RAC/RAC-单机)
查看>>
Truncate 表之恢复
查看>>
Oracle DG failover 后恢复
查看>>
mysql 主从同步配置
查看>>
为什么很多程序员都选择跳槽?
查看>>
mongdb介绍
查看>>
mongdb在java中的应用
查看>>
区块链技术让Yotta企业云盘为行政事业服务助力
查看>>
Yotta企业云盘更好的为媒体广告业服务
查看>>
Yotta企业云盘助力科技行业创高峰
查看>>
Yotta企业云盘更好地为教育行业服务
查看>>
Yotta企业云盘怎么帮助到能源化工行业
查看>>