網(wǎng)站首頁(yè) 編程語(yǔ)言 正文
回顧
上一篇文章我們介紹了切片slice的定義初始化、引用類型特征、如何使用數(shù)組切割成切片。
這篇文章介紹切片的生成make()、切片的追加append()、切片的復(fù)制copy()。對(duì)知識(shí)點(diǎn)進(jìn)行詳細(xì)介紹和應(yīng)用實(shí)戰(zhàn)。
加深理解
- 切片的本質(zhì):切片的本質(zhì)是一個(gè)框,框住了一塊連續(xù)的內(nèi)存
- 切片屬于引用類型,真正的數(shù)據(jù)都是保存在底層數(shù)組里的
- 切片可以簡(jiǎn)單理解為是快捷方式,修改會(huì)互相影響
- 判斷一個(gè)切片是否為空,使用len(s) == 0 判斷,不能使用 s==nil 判斷
生成切片 make
上需求:請(qǐng)定義一個(gè)長(zhǎng)度為5,容量為10的整型切片。
上代碼:
s1 := make([]int,5,10) fmt.Printf("s1:%v len(s1):%d cap(s1):%d\n", s1, len(s1), cap(s1))
打印結(jié)果:
分析:make()函數(shù)的第一個(gè)參數(shù)指定切片的數(shù)組類型,第二個(gè)參數(shù)指定切片的長(zhǎng)度,第三個(gè)參數(shù)指定切片的容量。
更好的理解長(zhǎng)度和容量
s1 := make([]int,5,10) fmt.Printf("s1:%v len(s1):%d cap(s1):%d\n", s1, len(s1), cap(s1)) s2 := make([]int, 0, 10) fmt.Printf("s2=%v len(s2)=%d cap(s2)=%d\n", s2, len(s2), cap(s2))
打印結(jié)果:
分析: 我們可以發(fā)現(xiàn)定義切片時(shí)元素的個(gè)數(shù)和長(zhǎng)度相關(guān),因?yàn)殚L(zhǎng)度就是元素的個(gè)數(shù)。
容量我們?cè)谙旅娼榻Bappend()時(shí),重點(diǎn)介紹一下。
切片引用類型實(shí)戰(zhàn)
上代碼
//切片 s3 := make([]int, 3, 3) s3 = []int{1, 2, 3} s4 := s3 //s3 s4都指向同一個(gè)底層數(shù)組 fmt.Println(s3, s4) //[1 2 3] [1 2 3] s3[0] = 1000 fmt.Println(s3, s4) //[1000 2 3] [1 2 3] fmt.Println("-----") //數(shù)組 a3 := [3]int{1, 2, 4} a4 := a3 fmt.Println(a3, a4) a3[0] = 1000 fmt.Println(a3, a4)
打印結(jié)果:
分析:通過(guò)上面的打印結(jié)果我們可以很直觀的看出來(lái),切片引用類型的本質(zhì),當(dāng)切片修改時(shí)會(huì)互相影響;而數(shù)組作為值類型,不會(huì)互相影響。
切片的遍歷
和數(shù)組一樣,用fori或者for range進(jìn)行遍歷即可。
s3 := make([]int, 3, 3) s3 = []int{1, 2, 3} for i := 0; i < len(s3); i++ { fmt.Println(s3[i]) } for i, v := range s3 { fmt.Println(i, v) }
append
首先,我們定義一個(gè)切片
s1 := []string{"北京", "上海", "大連", "佛山"} fmt.Printf("s1=%v len(s1)=%d cap(s1)=%d\n", s1, len(s1), cap(s1))
打印結(jié)果:
分析:我們發(fā)現(xiàn)切片的長(zhǎng)度和容量都是4
然后,我們使用append()函數(shù)追加一個(gè)元素
s1 := []string{"北京", "上海", "大連", "佛山"} fmt.Printf("s1=%v len(s1)=%d cap(s1)=%d\n", s1, len(s1), cap(s1)) s1 = append(s1, "唐山") //切片append()追加之后, fmt.Printf("s1=%v len(s1)=%d cap(s1)=%d\n", s1, len(s1), cap(s1))
打印結(jié)果:
分析:長(zhǎng)度由4變成5,我們很好理解;容量為什么會(huì)從4變成8呢?
這是Go語(yǔ)言對(duì)切片的自動(dòng)擴(kuò)容機(jī)制。append()追加元素,原來(lái)的底層數(shù)據(jù)容量不夠時(shí),go底層會(huì)把底層數(shù)組替換,是go語(yǔ)言的一套擴(kuò)容策略
我后面會(huì)單獨(dú)寫(xiě)一篇來(lái)講自動(dòng)擴(kuò)容策略是如何實(shí)現(xiàn)的。歡迎大家持續(xù)關(guān)注我的專欄Go語(yǔ)言學(xué)習(xí)專欄
多次追加
多次追加的概念很好理解,就是多次調(diào)用append()
舉個(gè)栗子:
s1 := []string{"北京", "上海", "大連", "佛山"} fmt.Printf("s1=%v len(s1)=%d cap(s1)=%d\n", s1, len(s1), cap(s1)) s1 = append(s1, "唐山") //切片append()追加之后, fmt.Printf("s1=%v len(s1)=%d cap(s1)=%d\n", s1, len(s1), cap(s1)) var s2 []string s2 = append(s1, "雄安") fmt.Printf("s2=%v len(s2)=%d cap(s2)=%d\n", s2, len(s2), cap(s2))
打印結(jié)果:
分析: s1經(jīng)過(guò)兩次append追加元素,賦值給了s2
追加多個(gè)元素
當(dāng)我們需要追加多個(gè)元素時(shí),難道只能像上面這樣多次調(diào)用append嗎?
當(dāng)然不是的。
舉個(gè)栗子:
s1 := []string{"北京", "上海", "大連", "佛山"} s3 := []string{"太原","石家莊"} var s4 []string s4 = append(s1,s3...) // ...表示拆開(kāi),將切片的值作為追加的元素 fmt.Printf("s4的值:%v",s4)
打印結(jié)果:
注意:append的第二個(gè)參數(shù),我們傳入了一個(gè)切片,需要在切片后寫(xiě)死...,表示將切片切割,將切片的值作為追加到第一個(gè)參數(shù)中的元素。
復(fù)制切片
下面演示兩種方式:
//定義切片s1 s1 := []int{1, 2, 3} //第一種方式:直接聲明變量 用=賦值 //s2切片和s1引用同一個(gè)內(nèi)存地址 var s2 = s1 //第二種方式:copy var s3 = make([]int, 3) copy(s3, s1) //使用copy函數(shù)將 參數(shù)2的元素復(fù)制到參數(shù)1 fmt.Println(s1, s2, s3) //都是[1 2 3]
打印結(jié)果:都返回[1 2 3]
注意:make和:=不能同時(shí)使用,這種是錯(cuò)誤的寫(xiě)法 :s3 := make([]int, 5)
聰明的小伙伴們是不是提出疑問(wèn)了呢?
既然結(jié)果一樣,為什么要引出copy這個(gè)函數(shù)呢?
咱們接著往下看,就知道所以然了。
//定義切片s1 s1 := []int{1, 2, 3} //第一種方式:直接聲明變量 用=賦值 //s2切片和s1引用同一個(gè)內(nèi)存地址 var s2 = s1 //第二種方式:copy var s3 = make([]int, 3) copy(s3, s1) //使用copy函數(shù)將 參數(shù)2的元素復(fù)制到參數(shù)1 s1[0] = 11 fmt.Printf("s1:%v s2:%v s3:%v",s1, s2, s3) //s1和s2是[11 2 3] s3是[1 2 3]
打印結(jié)果:
分析: 我們發(fā)現(xiàn)s1和s2是[11 2 3] s3是[1 2 3],說(shuō)明copy方法是復(fù)制了一份,開(kāi)辟了新的內(nèi)存空間,不再引用s1的內(nèi)存地址,這就是兩者的區(qū)別。
刪除元素
注意:刪除切片中的元素 不能直接刪除 可以組合使用分割+append的方式刪除切片中的元素
舉個(gè)栗子:比如切除s3中的元素2(下標(biāo)為1的元素)
s3 := []int{1, 2, 3} s3 = append(s3[:1], s3[2:]...) //第一個(gè)不用拆開(kāi) 原因是一個(gè)作為被接受的一方 是把后面的元素追加到第一個(gè) fmt.Println(s3)
打印結(jié)果:
注意:上面代碼段中有我添加的注釋:append()函數(shù)中第一個(gè)切片不用拆開(kāi),原因是一個(gè)作為被接受的一方,是把后面的元素追加到第一個(gè)切片中。
數(shù)組轉(zhuǎn)切片
a1 := [...]int{1,2,3} s1 := a1[:] fmt.Printf("a1類型:%T\ns1類型:%T",a1,s1)
打印結(jié)果:
實(shí)戰(zhàn)演練
猜想一下下面程序的運(yùn)行結(jié)果:
s1 := make([]int, 5, 10) for i := 0; i < 10; i++ { s1 = append(s1, i) } fmt.Println(s1)
先
不
要
看
答
案
.
.
.
打印結(jié)果:
分析: 我們靜下心來(lái)逐步推導(dǎo)就ok了:
- s1 := make([]int, 5, 10) 生成的是切片: [0 0 0 0 0]
- for循環(huán)中每次將自增的i值追加到上面的切片中
- 所以最終打印的結(jié)果是:[0 0 0 0 0 0 1 2 3 4 5 6 7 8 9]
總結(jié)
原文鏈接:https://juejin.cn/post/7068573594879524894
相關(guān)推薦
- 2023-01-18 Android?Intent通信詳細(xì)講解_Android
- 2024-02-17 阻塞IO、非阻塞IO、IO多路復(fù)用、AIO的區(qū)別
- 2022-05-29 C#實(shí)現(xiàn)文字轉(zhuǎn)語(yǔ)音功能_C#教程
- 2022-10-22 Python中的Unittest基本使用_python
- 2022-07-13 C# System.Web.HttpContext.Current.Server.MapPath 報(bào)
- 2022-11-30 關(guān)于頁(yè)面加載即執(zhí)行JQuery的三種方法小結(jié)_jquery
- 2022-07-24 Harbor高可用配置及倉(cāng)庫(kù)使用介紹_云其它
- 2022-06-14 C語(yǔ)言詳解冒泡排序?qū)崿F(xiàn)_C 語(yǔ)言
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運(yùn)行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲(chǔ)小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運(yùn)算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過(guò)濾器
- Spring Security概述快速入門(mén)
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯(cuò)誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實(shí)現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡(jiǎn)單動(dòng)態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對(duì)象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊(duì)列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支