最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Go數(shù)組與切片輕松掌握

 更新時(shí)間:2022年11月18日 15:47:00   作者:Mingvvv  
在Java的核心庫(kù)中,集合框架可謂鼎鼎大名:Array、List、Set等等,隨便拎一個(gè)出來(lái)都值得開(kāi)發(fā)者好好學(xué)習(xí)如何使用甚至是背后的設(shè)計(jì)源碼。雖然Go語(yǔ)言沒(méi)有如此豐富的容器類(lèi)型,但也有一些基本的容器供開(kāi)發(fā)者使用,接下來(lái)讓我們認(rèn)識(shí)一下這些容器類(lèi)型吧

在 Go 中,數(shù)組和切片的功能其實(shí)是類(lèi)似的,都是用來(lái)存儲(chǔ)一種類(lèi)型元素的集合。數(shù)組是固定長(zhǎng)度的,而切片的長(zhǎng)度是可以調(diào)整的

數(shù)組(array)

我們?cè)诼暶饕粋€(gè)數(shù)組的時(shí)候據(jù)必須要定義它的長(zhǎng)度,并且不能修改。

數(shù)組的長(zhǎng)度是其類(lèi)型的一部分:比如,[2]int 和 [4]int 是兩個(gè)不同的數(shù)組類(lèi)型。

初始化數(shù)組

// 1. 創(chuàng)建一維數(shù)組
// 元素都是默認(rèn)值
var arr1 [3]int
// 指定長(zhǎng)度并設(shè)置初始值
var arr2 = [3]int{1, 2, 3}
var arr3 [3]int = [3]int{1, 2, 3}
// 自動(dòng)推導(dǎo)數(shù)組長(zhǎng)度
var arr4 = [...]int{1, 2, 3}
// 指定特定下標(biāo)的元素的值,其他的為默認(rèn)值
var arr5 = [3]int{1: 9}
// 2. 創(chuàng)建多維數(shù)組 與一維數(shù)組類(lèi)似,不再贅述
var arr6 = [3][2]int{{1, 2}, {3, 4}, {5, 6}}
fmt.Println(arr1)
fmt.Println(arr2)
fmt.Println(arr3)
fmt.Println(arr4)
fmt.Println(arr5)
fmt.Println(arr6)

------結(jié)果----------------------------
[0 0 0]
[1 2 3]
[1 2 3]
[1 2 3]
[0 9 0]
[[1 2] [3 4] [5 6]]

數(shù)組賦值

var arr = [3]int{1, 2, 3}
fmt.Println(arr)
arr[2] = 9
fmt.Println(arr)

------結(jié)果----------------------------
[1 2 3]
[1 2 9]

遍歷數(shù)組

方法一:for 循環(huán)遍歷

var arr = [3]int{1, 2, 3}
for i := 0; i < len(arr); i++ {
	fmt.Println(arr[i])
}

------結(jié)果----------------------------
1
2
3

方法二:for range 循環(huán)遍歷

使用 index 和 value 分別接收每次循環(huán)到的位置的下標(biāo)和值

var arr = [3]int{1, 2, 3}
for index, value := range arr {
	fmt.Printf("index:%d value:%d\n", index, value)
}

------結(jié)果----------------------------
index:0 value:1
index:1 value:2
index:2 value:3

數(shù)組對(duì)比

數(shù)組比較的方法比較簡(jiǎn)單,使用 == 符號(hào)即可

var arr = [3]int{1, 2, 3}
var arr2 = [3]int{1, 2, 3}
fmt.Println(arr == arr2)
var arr3 = [...]int{1, 2, 3}
fmt.Println(arr == arr3)
var arr4 = [...]int{1, 2, 4}
fmt.Println(arr == arr4)

------結(jié)果----------------------------
true
true
false

不能比較長(zhǎng)度不同的數(shù)組類(lèi)型,否則編譯器會(huì)報(bào)錯(cuò),如下:

var arr = [3]int{1, 2, 3}
var arr5 = [...]int{1, 2}
fmt.Println(arr == arr5)

切片(slice)

切片的性質(zhì)

切片類(lèi)型的定義

type slice struct {
    array unsafe.Pointer //指向數(shù)組的指針
    len int //切片的長(zhǎng)度,可以理解為切片表示的元素的個(gè)數(shù)
    cap int //容量,指針?biāo)赶虻臄?shù)組長(zhǎng)度(從指針位置向后)
}

切片的特性

  • 切片是一個(gè)引用類(lèi)型,是對(duì)數(shù)組的一個(gè)連續(xù)片段的引用
  • 切片本身是一個(gè)結(jié)構(gòu)體,通過(guò)值拷貝傳遞
  • 切片的 cap 一定是大于等于 len 的

切片初始化

//直接聲明并賦值
s0 := []int{1, 2, 3, 4, 5}
//通過(guò)數(shù)組或者切片獲取
arr := [...]int{1, 2, 3, 4, 5}
s1 := s0[:] // 切片 s0 中的全部元素
s2 := s0[:2] // 切片 s0 第一個(gè)元素到第二個(gè)元素
s3 := arr[3:] // 數(shù)組 arr 從第四個(gè)元素開(kāi)始向后的所有元素
s4 := arr[0:0] // 創(chuàng)建一個(gè)空切片
//通過(guò) make(t Type, size ...IntegerType) 初始化,
//接受的第一個(gè) int 表示切片長(zhǎng)度,第二個(gè)表示容量大小。如果只有一個(gè)int參數(shù)則默認(rèn)長(zhǎng)度和容量是相同的
s5 := make([]int, 5) //創(chuàng)建一個(gè)長(zhǎng)度為 5 切片,
s6 := make([]int, 5, 8) //創(chuàng)建一個(gè)長(zhǎng)度為 5 容量為 8 的int型切片(長(zhǎng)度為5的部分會(huì)被初始化為默認(rèn)值)
fmt.Println(s0, s1, s2, s3, s4, s5 ,s6)

-------結(jié)果-----------------------------------
[1 2 3 4 5] [1 2 3 4 5] [1 2] [4 5] [] [0 0 0 0 0] [0 0 0 0 0]

切片賦值

和數(shù)組相同根據(jù) index 賦值

//直接聲明并賦值
s0 := []int{1, 2, 3, 4, 5}
fmt.Println(s0)
s0[0] = 999
fmt.Println(s0)

-------結(jié)果-----------------------------------
[1 2 3 4 5]
[999 2 3 4 5]

切片的容量

我們可以通過(guò) len(slice) 獲取一個(gè)切片的長(zhǎng)度,可以通過(guò) cap(slice) 獲取一個(gè)切片的容量。

容量:指針?biāo)赶虻臄?shù)組長(zhǎng)度(從指針位置向后),如何理解 從指針位置向后 這個(gè)意思,通過(guò)代碼觀察:

s0 := []int{1, 2, 3, 4, 5}
s1 := s0[1:3] //第二個(gè)元素到第三個(gè)元素
fmt.Printf("len: %d\n", len(s1))
fmt.Printf("cap: %d\n", cap(s1))
fmt.Println(s1 )

------結(jié)果---------------
len: 2
cap: 4
[2 3]

如上,s1 實(shí)際指向的數(shù)組是 s0 的數(shù)組的一個(gè)連續(xù)片段。

所有我們可以使用 cap 把切片 s1 指向的數(shù)組(指針向后,包含指針)的去拿不元素都獲取到:

s0 := []int{1, 2, 3, 4, 5}
s1 := s0[1:3]
s2 := s1[:cap(s1)]
fmt.Printf("len: %d\n", len(s2))
fmt.Printf("cap: %d\n", cap(s2))
fmt.Println(s2)

-------結(jié)果------------
len: 4
cap: 4
[2 3 4 5]

append以及擴(kuò)容

append 可以動(dòng)態(tài)地向切片中追加元素

s0 := []int{1, 2, 3, 4, 5}
s0 = append(s0, 6, 7, 8, 9, 10) //追加元素
fmt.Printf("len: %d\n", len(s0))
fmt.Printf("cap: %d\n", cap(s0))
fmt.Println(s0)
s1 := []int{11, 12, 13, 14, 15}
s0 = append(s0, s1...) //追加切片,切片需要解包
fmt.Printf("len: %d\n", len(s0))
fmt.Printf("cap: %d\n", cap(s0))
fmt.Println(s0)

len: 10

cap: 10

[1 2 3 4 5 6 7 8 9 10]

len: 15

cap: 20

[1 2 3 4 5 6 7 8 9 10 11 12 13 14 15]

我們可以發(fā)現(xiàn),在第二次和第三次追加元素的時(shí)候,切片的容量發(fā)生了變化,兩次都是擴(kuò)充為之前容量的兩倍。

但是一定都是兩倍擴(kuò)容嗎?事實(shí)上不是的,如以下代碼:

s0 := make([]int, 1000)
fmt.Printf("len: %d, cap: %d\n", len(s0), cap(s0))
s0 = append(s0, make([]int, 200)...)
fmt.Printf("len: %d, cap: %d\n", len(s0), cap(s0))
s0 = append(s0, make([]int, 400)...)
fmt.Printf("len: %d, cap: %d\n", len(s0), cap(s0))

-----結(jié)果--------------------------
len: 1000, cap: 1000
len: 1200, cap: 1536
len: 1600, cap: 2304

可以發(fā)現(xiàn)第一次擴(kuò)容后,容量變?yōu)?1536,第二次擴(kuò)容后容量又變成了 2304,并不是什么兩倍的關(guān)系。

通過(guò)查看 append 源碼中的容量計(jì)算部分

func growslice(et *_type, old slice, cap int) slice {
	...
newcap := old.cap
	doublecap := newcap + newcap
	if cap > doublecap {
		newcap = cap
	} else {
		const threshold = 256
		if old.cap < threshold {
			newcap = doublecap //小容量直接擴(kuò)容到兩倍容量
		} else {
			// Check 0 < newcap to detect overflow
			// and prevent an infinite loop.
			for 0 < newcap && newcap < cap {
				// Transition from growing 2x for small slices
				// to growing 1.25x for large slices. This formula
				// gives a smooth-ish transition between the two.
				//大容量取消了 1.25 倍擴(kuò)容,選擇了一個(gè)更為平滑的擴(kuò)容方案
				newcap += (newcap + 3*threshold) / 4
			}
			// Set newcap to the requested cap when
			// the newcap calculation overflowed.
			if newcap <= 0 {
				newcap = cap
			}
		}
	}
	var overflow bool
	var lenmem, newlenmem, capmem uintptr
	// Specialize for common values of et.size.
	// For 1 we don't need any division/multiplication.
	// For goarch.PtrSize, compiler will optimize division/multiplication into a shift by a constant.
	// For powers of 2, use a variable shift.
	switch {
	case et.size == 1:
		lenmem = uintptr(old.len)
		newlenmem = uintptr(cap)
		capmem = roundupsize(uintptr(newcap))
		overflow = uintptr(newcap) > maxAlloc
		newcap = int(capmem)
	case et.size == goarch.PtrSize:
		lenmem = uintptr(old.len) * goarch.PtrSize
		newlenmem = uintptr(cap) * goarch.PtrSize
		capmem = roundupsize(uintptr(newcap) * goarch.PtrSize)
		overflow = uintptr(newcap) > maxAlloc/goarch.PtrSize
		newcap = int(capmem / goarch.PtrSize)
	case isPowerOfTwo(et.size):
		var shift uintptr
		if goarch.PtrSize == 8 {
			// Mask shift for better code generation.
			shift = uintptr(sys.Ctz64(uint64(et.size))) & 63
		} else {
			shift = uintptr(sys.Ctz32(uint32(et.size))) & 31
		}
		lenmem = uintptr(old.len) << shift
		newlenmem = uintptr(cap) << shift
		capmem = roundupsize(uintptr(newcap) << shift)
		overflow = uintptr(newcap) > (maxAlloc >> shift)
		newcap = int(capmem >> shift)
	default:
		lenmem = uintptr(old.len) * et.size
		newlenmem = uintptr(cap) * et.size
		capmem, overflow = math.MulUintptr(et.size, uintptr(newcap))
		capmem = roundupsize(capmem)
		newcap = int(capmem / et.size)
	}
	...
    return slice{p, old.len, newcap}
}

從源碼中可以得知:

  • 當(dāng)需要的容量大于兩倍舊切片的容量時(shí),需要的容量
  • 就是新容量當(dāng)需要的容量小于兩倍舊切片的容量時(shí), 判斷是否舊切片的長(zhǎng)度, 如果小于 256 , 那么新的容量就是兩倍舊的容量,當(dāng)大于等于 256 時(shí), 會(huì)選擇一個(gè)過(guò)度算法 newcap += (newcap + 3*256) / 4 不斷增加,直至大于等于需要的容量
  • 特殊的一點(diǎn)是,后面的 capmem = roundupsize(uintptr(newcap) * et.size) 這個(gè)方法,做了內(nèi)存對(duì)齊,導(dǎo)致最后算出的容量大于等于推算出來(lái)的容量,至于內(nèi)存對(duì)齊都做了哪些操作,還有待研究。

到此這篇關(guān)于Golang數(shù)組與切片輕松掌握的文章就介紹到這了,更多相關(guān)Golang數(shù)組和切片內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Go語(yǔ)言高效編程的3個(gè)技巧總結(jié)

    Go語(yǔ)言高效編程的3個(gè)技巧總結(jié)

    Go語(yǔ)言是一種開(kāi)源編程語(yǔ)言,可輕松構(gòu)建簡(jiǎn)單、可靠且高效的軟件,下面這篇文章主要給大家分享介紹了關(guān)于Go語(yǔ)言高效編程的3個(gè)技巧,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2023-01-01
  • Go語(yǔ)言中最便捷的http請(qǐng)求包resty的使用詳解

    Go語(yǔ)言中最便捷的http請(qǐng)求包resty的使用詳解

    go語(yǔ)言雖然自身就有net/http包,但是說(shuō)實(shí)話用起來(lái)沒(méi)那么好用,resty包是go語(yǔ)言中一個(gè)非常受歡迎的http請(qǐng)求處理包,下面我們一起來(lái)學(xué)習(xí)一下resty的具體使用吧
    2025-03-03
  • go語(yǔ)言中五種字符串的拼接方式(小結(jié))

    go語(yǔ)言中五種字符串的拼接方式(小結(jié))

    字符串拼接在 golang 里面其實(shí)有很多種實(shí)現(xiàn),本文就詳細(xì)的介紹一下go語(yǔ)言中五種字符串的拼接方式以及性能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-12-12
  • golang interface判斷為空nil的實(shí)現(xiàn)代碼

    golang interface判斷為空nil的實(shí)現(xiàn)代碼

    這篇文章主要介紹了golang interface判斷為空nil的實(shí)現(xiàn)代碼,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2021-04-04
  • Go GORM版本2.0新特性介紹

    Go GORM版本2.0新特性介紹

    這篇文章主要為大家介紹了Go GORM版本2.0新特性的使用示例介紹,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-06-06
  • Go?代碼塊作用域變量遮蔽問(wèn)題解析

    Go?代碼塊作用域變量遮蔽問(wèn)題解析

    這篇文章主要為大家介紹了Go?代碼塊作用域變量遮蔽問(wèn)題解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-10-10
  • go mayfly開(kāi)源項(xiàng)目代碼結(jié)構(gòu)設(shè)計(jì)

    go mayfly開(kāi)源項(xiàng)目代碼結(jié)構(gòu)設(shè)計(jì)

    這篇文章主要為大家介紹了go mayfly開(kāi)源項(xiàng)目代碼結(jié)構(gòu)設(shè)計(jì)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-11-11
  • 使用Go構(gòu)建一款靜態(tài)分析工具Owl詳解

    使用Go構(gòu)建一款靜態(tài)分析工具Owl詳解

    Owl是一款開(kāi)源項(xiàng)目依賴(lài)分析工具,可以快速在指定的項(xiàng)目目錄下查找符合某些特征的源代碼文件或者依賴(lài)文件,這篇文章主要介紹了使用Go構(gòu)建一款靜態(tài)分析工具,需要的朋友可以參考下
    2022-06-06
  • 一文搞懂Go?Exec?僵尸與孤兒進(jìn)程

    一文搞懂Go?Exec?僵尸與孤兒進(jìn)程

    本文主要介紹了Go?Exec?僵尸與孤兒進(jìn)程,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-02-02
  • 通過(guò)手機(jī)案例理解Go設(shè)計(jì)模式之裝飾器模式的功能屬性

    通過(guò)手機(jī)案例理解Go設(shè)計(jì)模式之裝飾器模式的功能屬性

    這篇文章主要為大家介紹了Go設(shè)計(jì)模式之裝飾器模式的功能屬性,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-05-05

最新評(píng)論

台安县| 馆陶县| 辽阳县| 长泰县| 长治市| 芷江| 佛坪县| 图们市| 商水县| 加查县| 丹东市| 泾川县| 吉木萨尔县| 温州市| 体育| 康马县| 随州市| 德清县| 游戏| 建水县| 泗水县| 博客| 江津市| 盘锦市| 拜城县| 宁津县| 乐清市| 龙陵县| 东乌珠穆沁旗| 陇南市| 若尔盖县| 邹城市| 纳雍县| 齐齐哈尔市| 三穗县| 霸州市| 柘城县| 杂多县| 新河县| 海安县| 滕州市|