目錄
1、陣列的初始化" >1、陣列的初始化
2、數組的遍歷" >2、數組的遍歷
3、多維數組" >3、多維數組
4、陣列是值型別" >4、陣列是值型別
二、切片" >二、切片
支援自動擴容切片是一個" >。它非常靈活,支援自動擴容切片是一個
位址長度和" >,它的內部結構包含位址長度
三、切片与数组的区别
首頁 後端開發 Golang go語言中切片和陣列是什麼

go語言中切片和陣列是什麼

Dec 21, 2022 pm 07:17 PM
golang go語言 陣列 切片

在go語言中,數組是一個由固定長度的特定類型元素組成的序列,是同一種資料類型元素的集合,一個數組可以由零個或多個元素組成。和數組對應的類型是Slice(切片),切片是對數組的一個連續片段的引用,所以切片是一個引用類型,這個片段可以是整個數組,也可以是由起始和終止索引標識的一些項的子集,需要注意的是,終止索引標識的項目不包括在切片內。

go語言中切片和陣列是什麼

本教學操作環境:windows7系統、GO 1.18版本、Dell G3電腦。

一、陣列

陣列是同一種資料型態元素的集合。在Go語言中,陣列從宣告時就決定,使用時可以修改陣列成員,但是陣列大小不可變更。基本語法:

// 定义一个长度为3元素类型为int的数组a
var a [3]int
登入後複製

陣列的長度必須是常數,並且長度是陣列類型的一部分。一旦定義,長度不能變

1、陣列的初始化

(1)方法一

	var testArray [3]int               // 定义数组时,会初始化int类型为零值
	var cityArray = [3]string{"北京", "上海", "深圳"} // 使用指定的初始值完成初始化
登入後複製

(2)方法二

在一般情況下我們可以讓編譯器根據初始值的個數自行推斷數組的長度

var cityArray = [...]string{"北京", "上海", "深圳"}
登入後複製

(3)方法三

我們也可以使用指定索引值的方式來初始化數組,例如:

func main() {
	a := [...]int{1: 1, 3: 5}
	fmt.Println(a)                  // [0 1 0 5]
	fmt.Printf("type of a:%T\n", a) //type of a:[4]int
}
登入後複製

2、數組的遍歷

func main() {
	var a = [...]string{"北京", "上海", "深圳"}
	// 方法1:for循环遍历
	for i := 0; i < len(a); i++ {
		fmt.Println(a[i])
	}

	// 方法2:for range遍历
	for index, value := range a {
		fmt.Println(index, value)
	}
}
登入後複製

3、多維數組

Go語言是支援多維數組的,我們這裡以二維數組為例(數組中又嵌套數組)。

(1)二維陣列的定義

func main() {
	a := [3][2]string{
		{"北京", "上海"},
		{"广州", "深圳"},
		{"成都", "重庆"},
	}
	fmt.Println(a) //[[北京 上海] [广州 深圳] [成都 重庆]]
	fmt.Println(a[2][1]) //支持索引取值:重庆
}
登入後複製

#(2)二維陣列的遍歷

func main() {
	a := [3][2]string{
		{"北京", "上海"},
		{"广州", "深圳"},
		{"成都", "重庆"},
	}
	for _, v1 := range a {
		for _, v2 := range v1 {
			fmt.Printf("%s\t", v2)
		}
		fmt.Println()
	}
}
登入後複製

  • ##注意:
  • 多維數組只有第一層可以使用...來讓編譯器推導出陣列長度。例如:
  • a := [...][2]string{
    	{"北京", "上海"},
    	{"广州", "深圳"},
    	{"成都", "重庆"},
    }
    登入後複製

4、陣列是值型別

陣列是值類型,賦值和傳參會複製整個陣列。因此

改變副本的值,不會改變本身的值。

func modifyArray(x [3]int) {
	x[0] = 100
}

func modifyArray2(x [3][2]int) {
	x[2][0] = 100
}
func main() {
	a := [3]int{10, 20, 30}
	modifyArray(a) //在modify中修改的是a的副本x
	fmt.Println(a) //[10 20 30]
	b := [3][2]int{
		{1, 1},
		{1, 1},
		{1, 1},
	}
	modifyArray2(b) //在modify中修改的是b的副本x
	fmt.Println(b)  //[[1 1] [1 1] [1 1]]
}
登入後複製
注意:

陣列支援「==「、」!=」運算符,因為記憶體總是被初始化過的。 [n]*T表示指標陣列(這是一個陣列,裡面元素是一個個的指標)##* [n]T

表示陣列指標(這是一個

指標
,存的是一個陣列的記憶體位址)

二、切片

切片(Slice)是一個擁有相同類型元素的

可變長度的序列

。它是

基於陣列類型做的一層封裝

。它非常靈活,支援自動擴容切片是一個

引用類型

,它的內部結構包含位址長度

容量

。切片一般用於快速地操作一塊資料集合。 切片(slice)是對數組的一個連續片段的引用,所以切片是一個引用類型(因此更類似於C/C  中的數組類型,或者 Python 中的list 類型),這個片段可以是整個數組,也可以是起始和終止索引標識的一些項目的子集,需要注意的是,終止索引標識的項不包括在切片內。 Go語言中切片的內部結構包含地址、大小和容量,切片一般用於快速地操作一塊數據集合,如果將數據集合比作切糕的話,切片就是你要的“那一塊” ,切的過程包含從何處開始(切片的起始位置)及切多大(切片的大小),容量可以理解為裝切片的口袋大小。

1、切片的定義

聲明切片類型的基本語法如下:

var name []T

// name:表示变量名
// T:表示切片中的元素类型
登入後複製

舉個栗子:

2、切片的長度和容量##########切片擁有自己的長度和容量,我們可以透過使用內建的len()函數來求長度,使用內建的cap()函數求切片的容量。 ###############3、切片表達式#########切片表達式###從字串、陣列、指向陣列或切片的指標建構子字符串或切片###。它有兩種變體:一種###指定low和high兩個索引界限值的簡單的形式###,另一種是除了low和high索引界限值外###還指定容量的完整的形式###。 #########完整切片表達式沒啥用,這裡只講簡單切片表達式! ######
// 简单切片表达式
func main() {
	a := [5]int{1, 2, 3, 4, 5}
	s := a[1:3]  // s := a[low:high]
	fmt.Printf("s:%v len(s):%v cap(s):%v\n", s, len(s), cap(s))
}
登入後複製
###運行結果:###
s:[2 3] len(s):2 cap(s):4
登入後複製
#########(1)使用make()函數建構切片######我們上面都是基於陣列來創建的切片,如果需要動態的建立一個切片,我們就需要使用內建的make()函數,格式如下:###
make([]T, size, cap)
登入後複製
  • T:切片的元素类型
  • size:切片中元素的数量
  • cap:切片的容量

举个栗子:

func main() {
	a := make([]int, 2, 10)
	fmt.Println(a)      //[0 0]
	fmt.Println(len(a)) //2
	fmt.Println(cap(a)) //10
}
登入後複製

上面代码中a的内部存储空间已经分配了10个,但实际上只用了2个。 容量并不会影响当前元素的个数,所以len(a)返回2,cap(a)则返回该切片的容量。

(2)切片的本质

切片自己不拥有任何数据。它只是底层数组的一种表示。对切片所做的任何修改都会反映在底层数组中

切片的本质 就是对底层数组的封装,它包含了三个信息:底层数组的指针、切片的长度(len)和切片的容量(cap)

举个例子,现在有一个数组a := [8]int{0, 1, 2, 3, 4, 5, 6, 7},切片s1 := a[:5],相应示意图如下。

go語言中切片和陣列是什麼

切片s2 := a[3:6],相应示意图如下:

go語言中切片和陣列是什麼

如果你懂了切片的本质,那么试试下面这个题吧!

func main() {
	a := [5]int{1, 2, 3, 4, 5}
	s := a[1:3]  // s := a[low:high]
	fmt.Printf("s:%v len(s):%v cap(s):%v\n", s, len(s), cap(s))
	s2 := s[3:4]  // 索引的上限是cap(s)而不是len(s),可能认为cap是2?切片是从原数组中元素2开始切走的
	fmt.Printf("s2:%v len(s2):%v cap(s2):%v\n", s2, len(s2), cap(s2))
}
登入後複製

运行结果:

s:[2 3] len(s):2 cap(s):4
s2:[5] len(s2):1 cap(s2):1
登入後複製

s2什么鬼?[2 3][3:4]这个能运行?如果有这样的疑惑,说明你并没有认识到切片的本质,下面我们来看一个图:

注意切片的本质是一个指向底层数组的起点的指针切片len有效长度,以及cap容量

go語言中切片和陣列是什麼

上面是切片s生成的过程,现在又要切片取[3:4],从s的起点开始数,我们可以很容易看出来[3:4]是5。

(3)切片不能直接比较

切片之间是不能比较的,我们不能使用==操作符来判断两个切片是否含有全部相等元素。 切片唯一合法的比较操作是和nil比较。 一个nil值的切片并没有底层数组,一个nil值的切片的长度和容量都是0。但是我们不能说一个长度和容量都是0的切片一定是nil,例如下面的示例:

var s1 []int         //len(s1)=0;cap(s1)=0;s1==nil
s2 := []int{}        //len(s2)=0;cap(s2)=0;s2!=nil
s3 := make([]int, 0) //len(s3)=0;cap(s3)=0;s3!=nil
登入後複製

所以要判断一个切片是否是空的,要是用len(s) == 0来判断,不应该使用s == nil来判断。

注意:nil和空不是一个概念,nil的判断是有无底层数组,s2、s3初始化了的,其实是有底层数组的,s1只是声明,因此没有底层数组为nil。是否为空,则len是否为0为唯一判断条件。

(4)切片的赋值拷贝

下面的代码中演示了拷贝前后两个变量共享底层数组,对一个切片的修改会影响另一个切片的内容,这点需要特别注意。

func main() {
	s1 := make([]int, 3) //[0 0 0]
	s2 := s1             //将s1直接赋值给s2,s1和s2共用一个底层数组
	s2[0] = 100
	fmt.Println(s1) //[100 0 0]
	fmt.Println(s2) //[100 0 0]
}
登入後複製

(5)切片遍历

切片的遍历方式和数组是一致的,支持索引遍历for range遍历。

func main() {
	s := []int{1, 3, 5}

	for i := 0; i < len(s); i++ {
		fmt.Println(i, s[i])
	}

	for index, value := range s {
		fmt.Println(index, value)
	}
}
登入後複製

(6)append()方法为切片添加元素

Go语言的内建函数append()可以为切片动态添加元素。 可以一次添加一个元素,可以添加多个元素,也可以添加另一个切片中的元素(后面加…)。

func main(){
	var s []int
	s = append(s, 1)        // [1]
	
	s = append(s, 2, 3, 4)  // [1 2 3 4]
	
	s2 := []int{5, 6, 7}  
	s = append(s, s2...)    // [1 2 3 4 5 6 7]
}
// 这个...类似于python中的*args打散列表
登入後複製

注意: 通过var声明的零值切片可以在append()函数直接使用,无需初始化。

var s []int
s = append(s, 1, 2, 3)
登入後複製

没有必要像下面的代码一样初始化一个切片再传入append()函数使用

s := []int{}  // 没有必要初始化
s = append(s, 1, 2, 3)

var s = make([]int)  // 没有必要初始化
s = append(s, 1, 2, 3)
登入後複製

每个切片会指向一个底层数组,这个数组的容量够用就添加新增元素。当底层数组不能容纳新增的元素时,切片就会自动按照一定的策略进行“扩容”,此时该切片指向的底层数组就会更换。“扩容”操作往往发生在append()函数调用时,所以我们通常都需要用原变量接收append函数的返回值

(7)切片的扩容策略

可以通过查看$GOROOT/src/runtime/slice.go源码,其中扩容相关代码如下:

newcap := old.cap
doublecap := newcap + newcap
if cap > doublecap {
	newcap = cap
} else {
	if old.len < 1024 {
		newcap = doublecap
	} else {
		// Check 0 < newcap to detect overflow
		// and prevent an infinite loop.
		for 0 < newcap && newcap < cap {
			newcap += newcap / 4
		}
		// Set newcap to the requested cap when
		// the newcap calculation overflowed.
		if newcap <= 0 {
			newcap = cap
		}
	}
}
登入後複製

go語言中切片和陣列是什麼

(8) 使用copy()函数复制切片

func main() {
	a := []int{1, 2, 3, 4, 5}
	b := a
	fmt.Println(a) //[1 2 3 4 5]
	fmt.Println(b) //[1 2 3 4 5]
	b[0] = 1000
	fmt.Println(a) //[1000 2 3 4 5]
	fmt.Println(b) //[1000 2 3 4 5]
}
登入後複製

由于切片是引用类型,所以a和b其实都指向了同一块内存地址。修改b的同时a的值也会发生变化

Go语言内建的copy()函数可以迅速地将一个切片的数据复制到另外一个切片空间中,copy()函数的使用方法如下:

func main() {
	// copy()复制切片
	a := []int{1, 2, 3, 4, 5}
	c := make([]int, 5, 5)
	copy(c, a)     //使用copy()函数将切片a中的元素复制到切片c
	fmt.Println(a) //[1 2 3 4 5]
	fmt.Println(c) //[1 2 3 4 5]
	c[0] = 1000
	fmt.Println(a) //[1 2 3 4 5]
	fmt.Println(c) //[1000 2 3 4 5] // 再对切片c操作,就不会影响a了
}
登入後複製

(9)从切片中删除元素

Go语言中并没有删除切片元素的专用方法,我们可以使用切片本身的特性来删除元素。 代码如下:

func main() {
	// 从切片中删除元素
	a := []int{30, 31, 32, 33, 34, 35, 36, 37}
	// 要删除索引为2的元素
	a = append(a[:2], a[3:]...) // 把index=2之后的切片和index=2之前的切片拼接在一起
	fmt.Println(a) //[30 31 33 34 35 36 37]
}
登入後複製

切片a中删除索引为index的元素,操作方法是a = append(a[:index], a[index+1:]...)

(10)内存优化

切片持有对底层数组的引用。只要切片在内存中,数组就不能被垃圾回收。在内存管理方面,这是需要注意的。让我们假设我们有一个非常大的数组,我们只想处理它的一小部分。然后,我们由这个数组创建一个切片,并开始处理切片。这里需要重点注意的是,在切片引用时数组仍然存在内存中。

一种解决方法是使用上面的copy函数,根据切片生成一个一模一样的新切片。这样我们可以使用新的切片,原始数组可以被垃圾回收。

package mainimport (
    "fmt")func countries() []string {
    a := []string{1, 2, 3, 4, 5}
    b := a[:len(a)-2]
    c := make([]string, len(b))
    copy(c, b) // 将b的内容copy给c
    return c}func main() {
    d := countries()
    fmt.Println(d)
 }
登入後複製

b := a[:len(a)-2] 创建一个去掉a的尾部 2 个元素的切片 b,在上述程序的 11 行,将 切片b 复制到 切片c。同时在函数的下一行返回 切片c。现在 a 数组可以被垃圾回收, 因为数组a不再被引用。

三、切片与数组的区别

Go 数组与像 C/C++等语言中数组略有不同:

1. Go 中的数组是值类型,换句话说,如果你将一个数组赋值给另外一个数组,那么,实际上就是将整个数组拷贝一份。因此,在 Go 中如果将数组作为函数的参数传递的话,那效率就肯定没有传递指针高了。

2. 数组的长度也是类型的一部分,这就说明[10]int和[20]int不是同一种数据类型。并且Go 语言中数组的长度是固定的,且不同长度的数组是不同类型,这样的限制带来不少局限性。

3. 而切片则不同,切片(slice)是一个拥有相同类型元素的可变长序列,可以方便地进行扩容和传递,实际使用时比数组更加灵活,这也正是切片存在的意义。而且切片是引用类型,因此在当传递切片时将引用同一指针,修改值将会影响其他的对象。

【相关推荐:Go视频教程编程教学

以上是go語言中切片和陣列是什麼的詳細內容。更多資訊請關注PHP中文網其他相關文章!

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

<🎜>:泡泡膠模擬器無窮大 - 如何獲取和使用皇家鑰匙
3 週前 By 尊渡假赌尊渡假赌尊渡假赌
北端:融合系統,解釋
3 週前 By 尊渡假赌尊渡假赌尊渡假赌

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

熱門話題

Java教學
1664
14
CakePHP 教程
1423
52
Laravel 教程
1320
25
PHP教程
1269
29
C# 教程
1249
24
在Go語言中使用Redis Stream實現消息隊列時,如何解決user_id類型轉換問題? 在Go語言中使用Redis Stream實現消息隊列時,如何解決user_id類型轉換問題? Apr 02, 2025 pm 04:54 PM

Go語言中使用RedisStream實現消息隊列時類型轉換問題在使用Go語言與Redis...

GoLand中自定義結構體標籤不顯示怎麼辦? GoLand中自定義結構體標籤不顯示怎麼辦? Apr 02, 2025 pm 05:09 PM

GoLand中自定義結構體標籤不顯示怎麼辦?在使用GoLand進行Go語言開發時,很多開發者會遇到自定義結構體標籤在�...

Go語言中哪些庫是由大公司開發或知名的開源項目提供的? Go語言中哪些庫是由大公司開發或知名的開源項目提供的? Apr 02, 2025 pm 04:12 PM

Go語言中哪些庫是大公司開發或知名開源項目?在使用Go語言進行編程時,開發者常常會遇到一些常見的需求,�...

Golang的目的:建立高效且可擴展的系統 Golang的目的:建立高效且可擴展的系統 Apr 09, 2025 pm 05:17 PM

Go語言在構建高效且可擴展的系統中表現出色,其優勢包括:1.高性能:編譯成機器碼,運行速度快;2.並發編程:通過goroutines和channels簡化多任務處理;3.簡潔性:語法簡潔,降低學習和維護成本;4.跨平台:支持跨平台編譯,方便部署。

在Go編程中,如何正確管理Mysql和Redis的連接與釋放資源? 在Go編程中,如何正確管理Mysql和Redis的連接與釋放資源? Apr 02, 2025 pm 05:03 PM

Go編程中的資源管理:Mysql和Redis的連接與釋放在學習Go編程過程中,如何正確管理資源,特別是與數據庫和緩存�...

Golang vs. Python:性能和可伸縮性 Golang vs. Python:性能和可伸縮性 Apr 19, 2025 am 12:18 AM

Golang在性能和可擴展性方面優於Python。 1)Golang的編譯型特性和高效並發模型使其在高並發場景下表現出色。 2)Python作為解釋型語言,執行速度較慢,但通過工具如Cython可優化性能。

多進程日誌寫入如何保證並發安全又高效? 多進程日誌寫入如何保證並發安全又高效? Apr 02, 2025 pm 03:51 PM

高效處理多進程日誌寫入的並發安全問題多進程同時寫入同一個日誌文件,如何保證並發安全且高效?這是一個...

Golang和C:並發與原始速度 Golang和C:並發與原始速度 Apr 21, 2025 am 12:16 AM

Golang在並發性上優於C ,而C 在原始速度上優於Golang。 1)Golang通過goroutine和channel實現高效並發,適合處理大量並發任務。 2)C 通過編譯器優化和標準庫,提供接近硬件的高性能,適合需要極致優化的應用。

See all articles