go基础语法之切片

go基础语法之切片

切片是什么

go的数组,因定义后,长度固定,不可增删其中元素,不方便,

go的切片slice,是一个具有相同类型元素的可变长序列的组合,是基于数组类型做的一层封装,灵活,支持自动扩容

go切片是引用类型,内部结构:包括:地址、长度、容量,切片一般用于快速的操作一块数据集合

切片的定义

语法与示例

语法:

1
2
3
4
5
var name []T

name为变量名
T为元素类型
和数组不同之处[]内没有指定长度也没有...利用编译器推测长度

示例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package main

import "fmt"
func main()  {
	var a []string // 声明一个字符串切片,默认是空

	var b = []int{}  // 声明一个整型切片并初始化,没赋值也是空
	var c = []bool{false,true}
	var d = []bool{false,true}

	fmt.Println(a)
	fmt.Println(b)
	fmt.Println(c)

	fmt.Println(a == nil) // 只有a等于nil
	fmt.Println(b == nil)
	fmt.Println(c == nil)
	fmt.Println(d == nil)
	// 切片是引用类型,不支持直接比较,只能和nil比较

}
D:\workstation\mycode\gocode\src\learngo\basic_grammar\06slice>go run main.go
[]
[]
[false true]

true
false
false
false

切片的长度和容量

切片有自己的长度、len()函数可以求得;cap()函数可以求切片的容量;

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
package main

import "fmt"

func main() {


	// 1,简单的切片表达式,s := a[low:high]
	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

切片表达式

切片表达式从字符串、数组、指向数组或切片的指针构造子字符串或切片、有2种变体;

  1. 指定low和high2的索引界限值
  2. 除了指定low和high还指定容量的完整形式,加上max

1、简单切片表达式:

切片底层就是数组、可以基于数组通过切片表达式,得到切片;切片表达式中的low和high表示一个索引范围,左包含、右不包含;例如:下面代码从数组a,选出1<=索引值<3的元素组成切片s,得到切片长度=high-low,容量等于得到的切片的底层数组的容量(但要从切的位置开始数,如下,从a的第一个下标开始数,容量是4)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
package main

import "fmt"

func main() {


	// 1,简单的切片表达式,s := a[low:high]
	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
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41

package main

import "fmt"

func main() {
    a := [5]int{1, 2, 3, 4, 5}
// 2, 切片表达式的省略
	s1 := a[2:]
	s2 := a[:3]
	s3 := a[:]


	fmt.Printf("%v", s1)
	fmt.Printf("%v", s2)
	fmt.Printf("%v", s3)
	fmt.Println("")
    
    
	// 3,对切片再切片时,high的上限边界是切片的容量,而不是长度!
	// 在切片s 的基础上,再切出一个s4,s是[2 3]
	// s4的high上限是s的容量,即4,而low是从0开始计算,即s的0是2,从这里开始计算,那么s的3,就对应a的5,所以s4就是一个5,长度为1,容量为1
	// a  1 2 3 4 5
	// s    2 3
	// s4

	s4 := s[3:4]
	fmt.Printf("s4:%v len(s4):%v cap(s4):%v \n", s4, len(s4), cap(s4))

	// 4,完整的切片表示式: a[low:high:max]
	// 	a := [5]int{1, 2, 3, 4, 5}
	// max-low就是该切片的cap容量,max的作用是限制切片的容量,没有max,默认会到数组的最后一个元素

	t := a[1:3:5]
	fmt.Printf("t值%v t长度%v t容量%v\n", t, len(t), cap(t))
    
}   

[3 4 5][1 2 3][1 2 3 4 5]
s4:[5] len(s4):1 cap(s4):1
t值[2 3] t长度2 t容量4

make函数构造切片

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
package main

import "fmt"

func main() {

    	// 5,使用make()函数构造切片,而不是基于数组去切
	// 语法: make([]T ,size, cap) T 是元素类型,size是元素数量,cap是切片的容量,即元素数量的上限

	slice1 := make([]int, 2, 10)
	fmt.Println(slice1)
	fmt.Println(len(slice1)) // 长度为2
	fmt.Println(cap(slice1)) // 容量为10
    
}   
[0 0]
2
10

切片本质

切片的本质:对数组进行的封装

切片:包含3个信息:底层数组的指针、切片的长度len、切片的容量cap

image-20201229182003684

判断切片是否为空

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
package main

import "fmt"

func main() {
	// 6,判断切片是否为空,用len(s) == 0判断,而不是s == nil

	slice2 := make([]int,0,10)

	if len(slice2) == 0 {
		fmt.Println("slice2 is null")


	}
    
}   


slice2 is null

切片不能直接比较

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
package main

import "fmt"

func main() {

	// 7, 切片不能直接比较,切片唯一能比较的是nil,容量和长度都是0的切片,不一定义等于nil,反过来,则成立
	// var slice3 []int // len=0 cap =0 slice3=nil
	// slice4 := []int{} // len=0 cap =0 ,slice4!=nil
	// slice5 := make([]int,0) // // len=0 cap =0 ,slice5!=nil
    
}   

切片的赋值拷贝

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
package main

import "fmt"

func main() {

    	// 8,切片的赋值拷贝,切片是引用类型,基于同一个数组的切片,一个改了,会影响其他切片
	slice6 := make([]int,3)

	slice7 := slice6 // 此时6个7公用一个底层数组

	slice7[0] = 233
	fmt.Println(slice6)
	fmt.Println(slice7)
}   

[233 0 0]
[233 0 0]

切片遍历

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
package main

import "fmt"

func main() {

	// 9,切片的遍历,和数组的遍历一致,支持for和for range遍历
	// for i := 0, i < len(slice6); i++ {
	// 	fmt.Println(i, slice6[i] )
	// }

	for index,value := range slice6 {
		fmt.Println(index,value)
	}
    
}   
0 233
1 0
2 0

append方法给切片添加元素

根据打印结果,看出,从一个空值的切片开始算,默认扩容策略是1,2,4,8,16,前一次的2倍,其指针指向的内存地址,也会随之扩容,而变化

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
package main

import "fmt"

func main() {

	// 切片的追加

	var s []int
	s = append(s, 1) // 通过var定义的空值切片,可以在append值直接追加,无需初始化
	fmt.Println(s)
	s = append(s, 2, 3, 4)
	fmt.Println(s)

	// 切片的自动扩容
	// 从一个空值的切开开始算,默认扩容策略是1,2,4,8,16,前一次的2倍
	// 每个切片都会指向一个底层数组,当底层数组不能够容纳新元素时,切片会根据”策略“进行扩容,此时切片指向的数组指针,就会发生改变

	var slice []int
	for i := 0; i < 10; i++ {
		slice = append(slice, i)
		fmt.Printf("%v %d %d %p\n", slice, len(slice), cap(slice), slice)
	}
    
    
	// 切片,一次追加多个元素

	slice = append(slice,666,233,999)
	fmt.Println(slice)
}

D:\workstation\mycode\gocode\src\learngo\basic_grammar\06slice>go run main.go
[1]
[1 2 3 4]
[0] 1 1 0xc000012120
[0 1] 2 2 0xc000012140
[0 1 2] 3 4 0xc00000a460
[0 1 2 3] 4 4 0xc00000a460
[0 1 2 3 4] 5 8 0xc0000102c0
[0 1 2 3 4 5] 6 8 0xc0000102c0
[0 1 2 3 4 5 6] 7 8 0xc0000102c0
[0 1 2 3 4 5 6 7] 8 8 0xc0000102c0
[0 1 2 3 4 5 6 7 8] 9 16 0xc000018100
[0 1 2 3 4 5 6 7 8 9] 10 16 0xc000018100
[0 1 2 3 4 5 6 7 8 9 666 233 999]

切片的扩容策略

对应源码:$GOROOT/src/runtime/slice.go

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
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
			}
		}
	}

copy函数复制切片

1、切片是引用型

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package main

import "fmt"

func main() {



	// copy复制切片demo

	a := []int{1,3,5,7,9}
	b := a
	fmt.Println(a)
	fmt.Println(b)
	b[0] = 100
	fmt.Println(a)
	fmt.Println(b)
}
// 切片是引用型,a和b切片,指向的都是同一个内存地址,改一个,另一个也会变,
D:\workstation\mycode\gocode\src\learngo\basic_grammar\06slice>go run main.go
[1 3 5 7 9]
[1 3 5 7 9]
[100 3 5 7 9]
[100 3 5 7 9]

2、对切片进行复制,使得对副本的修改,不影响原切片

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package main

import "fmt"

func main() {



	// copy复制切片demo

	a := []int{1,3,5,7,9}
	b := make([]int,5,5)
	copy(b,a)
	fmt.Println(a)
	fmt.Println(b)
	b[0] = 100
	fmt.Println(a)
	fmt.Println(b)
}
// 可以看到,修改b,对a无影响,因为b是a的另一份数据拷贝,指向不同内存地址
D:\workstation\mycode\gocode\src\learngo\basic_grammar\06slice>go run main.go
[1 3 5 7 9]
[1 3 5 7 9]
[1 3 5 7 9]
[100 3 5 7 9]

从切片中删除元素

go本身不支持切片,但利用append函数,可以实现切片中元素的删除

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
package main

import "fmt"

func main() {

	// 切片的元素删除demo
	a := []int{1, 4, 5, 7, 8, 9}
	fmt.Println(a)
	// 现删除5,其索引为2
	a = append(a[:2], a[3:]...)
	fmt.Println(a)
	// 通过的格式:要删除a切片中,某个index的元素:a = append(a[:index],a[index+1:]...)
}


D:\workstation\mycode\gocode\src\learngo\basic_grammar\06slice>go run main.go
[1 4 5 7 8 9]
[1 4 7 8 9]

练习

  1. 请写出下面代码的输出结果。

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    
    func main() {
     var a = make([]string, 5, 10)
     for i := 0; i < 10; i++ {
         a = append(a, fmt.Sprintf("%v", i))
     }
     fmt.Println(a)
    }
       
    // 测试如下:
    // 原本长度5,容量10,前五个是默认的空字符串"",后五个无
    // 经过for循环,追加了10个字符串,长度变为15,容量因扩容策略,而翻倍,变成了20
    package main
       
    import "fmt"
       
    func main() {
       
     // 练习1
       
         var a = make([]string, 5, 10)
         for i := 0; i < 10; i++ {
             a = append(a, fmt.Sprintf("%v", i))
         }
         fmt.Println(a)
         fmt.Printf("len of a:%v", len(a))
         fmt.Printf("cap of a:%v", cap(a))
    }    
    D:\workstation\mycode\gocode\src\learngo\basic_grammar\06slice>go run main.go
    [     0 1 2 3 4 5 6 7 8 9]
    len of a:15cap of a:20
    
  2. 2.请使用内置的sort包对数组var a = [...]int{3, 7, 8, 9, 1}进行排序(附加题,自行查资料解答)

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    
    package main
       
    import (
     "fmt"
     "sort"
    )
       
    func main() {
       
     // 练习2
     var a = [...]int{3, 7, 8, 9, 1}
     //升序
       
     sort.Ints(a[:])
     fmt.Println(a)
     // 降序
     sort.Sort(sort.Reverse(sort.IntSlice(a[:])))
     fmt.Println(a)
    }
    D:\workstation\mycode\gocode\src\learngo\basic_grammar\06slice>go run main.go
    [1 3 7 8 9]
    [9 8 7 3 1]
    
updatedupdated2020-12-312020-12-31
加载评论