golang 数组和切片
本文是Mihalis Tsoukalos的“围棋”系列的一部分:
- 第1部分: 在Go中创建随机,安全的密码
- 第2部分: 在Go中构建并发TCP服务器 ,
- 第3部分: 在Go中复制文件的3种方法
在本系列的第四篇文章中,我将解释Go数组和切片,如何使用它们以及为什么通常要选择一个而不是另一个。
数组
数组是编程语言中最流行的数据结构之一,主要有两个原因:数组简单易懂,并且可以存储许多不同类型的数据。
您可以声明一个名为anArray
的Go数组,该数组存储以下四个整数:
anArray := [ 4 ] int { - 1 , 2 , 0 , - 4 }
数组的大小应在其类型之前声明,数组的大小应在其元素之前定义。 len()
函数可以帮助您找到任何数组的长度。 上面数组的大小为4。
for
循环。
但是,正如您将在下面看到的那样,Go的range
关键字使您可以更优雅地访问数组或切片的所有元素。
最后,这是定义二维数组的方法:
twoD
:=
[
3
][
3
]
int
{
{
1
,
2
,
3
},
{
6
,
7
,
8
},
{
10
,
11
,
12
}}
arrays.go
源文件说明了Go数组的用法。 arrays.go
最重要的代码是:
for
i
:=
0
;
i <
len
( twoD
);
i
++
{
k
:= twoD
[
i
]
for j
:=
0
; j <
len
( k
); j
++
{
fmt
. Print
( k
[ j
],
" "
)
}
fmt
. Println
()
}
for _
, a
:=
range twoD
{
for _
, j
:=
range a
{
fmt
. Print
( j
,
" "
)
}
fmt
.
Println
()
}
这显示了如何使用for
循环和range
关键字遍历数组的元素。 arrays.go
的其余代码显示了如何将数组作为参数传递给函数。
以下是arrays.go
的输出:
$
go run arrays
.
go
Before change
():
[
-
1
2
0
-
4
]
After change
():
[
-
1
2
0
-
4
]
1
2
3
6
7
8
10
11
12
1
2
3
6
7
8
10
11
12
此输出表明,对函数内部的数组所做的更改在函数退出后丢失。
数组的缺点
Go数组有许多缺点,这会让您重新考虑在Go项目中使用它们。 首先,定义数组后无法更改其大小,这意味着Go数组不是动态的。 简而言之,如果您需要向一个没有剩余空间的数组中添加元素,则需要创建一个更大的数组,并将旧数组的所有元素复制到新数组中。 其次,当您将数组作为参数传递给函数时,实际上是传递了数组的副本,这意味着对函数内部的数组所做的任何更改都会在函数退出后丢失。 最后,将大型数组传递给函数可能非常慢,这主要是因为Go必须创建数组的副本。
解决所有这些问题的方法是使用Go slice。
切片
Go切片类似于Go阵列,没有缺点。 首先,您可以使用append()
函数append()
元素添加到现有切片中。 此外,Go切片是使用数组在内部实现的,这意味着Go会为每个切片使用基础数组。
切片具有不总是相同的容量属性和长度属性。 切片的长度与具有相同元素数的数组的长度相同,可以使用len()
函数找到它。 切片的容量是当前已为该切片分配的空间,可以使用cap()
函数找到它。
由于切片的大小是动态的,因此如果切片空间不足(这意味着在您尝试向阵列中添加另一个元素时,数组的当前长度与其容量相同),Go会自动将其当前容量加倍以使容纳更多元素的空间,并将请求的元素添加到数组中。
此外,切片是通过对函数的引用传递的,这意味着实际传递给函数的是slice变量的内存地址,并且对函数内部切片的任何修改在函数退出后都不会丢失。 结果,将大切片传递给函数比将具有相同数量元素的数组传递给相同函数要快得多。 这是因为Go不必复制切片,它只会传递slice变量的内存地址。
go slice在slice.go
进行了说明,其中包含以下代码:
package main
import
(
"fmt"
)
func negative
( x
[]
int
)
{
for
i
, k
:=
range x
{
x
[
i
]
=
- k
}
}
func printSlice
( x
[]
int
)
{
for _
, number
:=
range x
{
fmt
. Printf
(
"%d "
, number
)
}
fmt
. Println
()
}
func main
()
{
s
:=
[]
int
{
0
,
14
,
5
,
0
,
7
,
19
}
printSlice
( s
)
negative
( s
)
printSlice
( s
)
fmt
. Printf
(
"Before. Cap: %d, length: %d \n "
,
cap
( s
),
len
( s
))
s
= append
( s
,
-
100
)
fmt
. Printf
(
"After. Cap: %d, length: %d \n "
,
cap
( s
),
len
( s
))
printSlice
( s
)
anotherSlice
:=
make
([]
int
,
4
)
fmt
. Printf
(
"A new slice with 4 elements: "
)
printSlice
( anotherSlice
)
}
切片定义和数组定义之间的最大区别在于,您无需指定切片的大小,该大小由要放入切片中的元素数决定。 另外, append()
函数允许您向现有切片中添加元素-请注意,即使切片的容量允许您向该切片中添加元素,也不会修改其长度,除非您调用append()
。 printSlice()
函数是用于打印其slice参数的元素的辅助函数,而negative()
函数则处理其slice参数的所有元素。
slice.go
的输出是:
$
go run slice
.
go
0
14
5
0
7
19
0
-
14
-
5
0
-
7
-
19
Before
. Cap
:
6
, length
:
6
After
. Cap
:
12
, length
:
7
0
-
14
-
5
0
-
7
-
19
-
100
A
new slice with
4 elements
:
0
0
0
0
请注意,当您创建一个新的slice并为给定数量的元素分配内存空间时,Go会自动将所有元素初始化为其类型的零值,在这种情况下为0。
用切片引用数组
Go允许您使用[:]
表示法引用带有切片的现有数组。 在这种情况下,您对slice函数的任何更改都将传播到数组中,这在refArray.go
说明。 请记住, [:]
表示法不会创建数组的副本,而只是创建数组的引用。
refArray.go
最有趣的部分是:
func main
()
{
anArray
:=
[
5
]
int
{
-
1
,
2
,
-
3
,
4
,
-
5
}
refAnArray
:= anArray
[:]
fmt
. Println
(
"Array:"
, anArray
)
printSlice
( refAnArray
)
negative
( refAnArray
)
fmt
.
Println
(
"Array:"
, anArray
)
}
refArray.go
的输出是:
$
go run refArray
.
go
Array
:
[
-
1
2
-
3
4
-
5
]
-
1
2
-
3
4
-
5
Array
:
[
1
-
2
3
-
4
5
]
因此, anArray
数组的元素由于对它的切片引用而发生了变化。
摘要
尽管Go同时支持数组和切片,但现在应该很清楚,您将很可能使用切片,因为它们比Go数组更通用,功能更强大。 在少数情况下,您将需要使用数组而不是切片。 最明显的是,当您完全确定需要存储固定数量的元素时。
您可以在GitHub上找到arrays.go
, slice.go
和refArray.go
的Go代码。
如果您有任何疑问或反馈,请在下面发表评论或在Twitter上与我联系。
翻译自: https://opensource.com/article/18/7/introduction-go-arrays-and-slices
golang 数组和切片