GO语言学习-Day2

176 阅读8分钟

GO语言容器

数组

声明:

  • var 数组名 [数组大小] T其中T可以为任意类型,也可以是数组本身,当类型为数组本身时,可以实现多维数组

初始化:

  • var arr = [2]int{1,2} 或者 arr := [2]int{1,2}。 如果不确定数组的初始化长度也可以这样写 var arr = [...]int{1,2} 或者 arr := [...]int{1,2}

数组的遍历:

  • 用for循环即可
arr := [...]int{1, 2, 3, 4, 5, 6}

for index,value:=range arr{

   fmt.Println(index,value)

}

注意:

  • 数组在go语言中是一个值类型,也就是说当我们进行数组赋值或者数组传参时,实际上都会拷贝一份新的数组。我们在拷贝数组上做任何改变都不会影响到原数组。
  • 数组的类型也包括长度,通俗地说:两个int数组的长度不同,那么它们不是同一个类,数组的类型包括了数组的长度,这也是Go和其他语言不同的地方

二维数组

获取二维数组的行:len(nums) 获取二维数组的列:len(nums[0])

切片

Go语言切片的内部结构包含地址、大小和容量,是建立在数组类型之上的抽象,提供了更加强大的功能。底层是一个数组,切片是对数组连续片段的一个引用,所以切片是引用类型。

声明:

  • 切片的声明和数组非常类似,但是它们是完全不同的,Go语言中的数组是基本类型,声明后就会开辟一块内存空间并初始化为零值,是可以直接使用的。而切片是一个引用类型,声明后并没有开辟内存空间,此时是nil(Java中的null),必须初始化之后才能使用才会分配给切片具体的内存空间。与数组相比切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大。

    声明格式:var identifier []type

//切片的声明并不会为其分配内存空间
var slice []int
var slice_empty []int

//切片初始化会为其分配相应的内存空间
var slice_int = []int{}

fmt.Println(slice,slice_empty,slice_int)
fmt.Println(slice_empty == slice_int)//报错:slice can only be compared to nil,意思为切片只能与nil做比较

make函数

我们还可以通过内建函数make()来直接获取切片,格式如下:make([]T, len, cap) T

  1. [ ]T:要创建的切片类型
  2. len:要创建的切片长度
  3. cap:要创建的切片容量 当我们使用make函数的时候我们一定为其进行了内存分配的操作

append函数

扩容机制:Go语言内建的append()方法可以为切片动态的添加元素。每个切片都会指向一块内存空间,这片空间能够容纳一定数量的元素(cap),当空间不够时,切片就会进行扩容。

func main() {
   //切片的声明,并不会为其分配内存空间
   var slice []int
   for i := 0; i < 10;i ++{
      slice = append(slice,i)
      fmt.Printf("len: %d \t cap: %d pointer:%p \n",len(slice),cap(slice),slice)
   }
   fmt.Print(slice)
}

输出结果:

len: 1   cap: 1 pointer:0xc000018070 
len: 2   cap: 2 pointer:0xc000018090 
len: 3   cap: 4 pointer:0xc00001a040 
len: 4   cap: 4 pointer:0xc00001a040 
len: 5   cap: 8 pointer:0xc00001e100 
len: 6   cap: 8 pointer:0xc00001e100 
len: 7   cap: 8 pointer:0xc00001e100 
len: 8   cap: 8 pointer:0xc00001e100 
len: 9   cap: 16 pointer:0xc000014080 
len: 10  cap: 16 pointer:0xc000014080 
[0 1 2 3 4 5 6 7 8 9]
  1. 可以看出每次切片在容量不足时都会扩容,而扩容的容量是原来的两倍
  2. 每次扩容完之后切片的内存地址就会改变,本质上就是在内存中开辟了一块新的连续内存空间,将旧的切片拷贝到新的内存空间中

copy函数

我们使用Go语言的内建函数copy()时,可以进行切片的复制,使用格式如下

copy(des []T,src []T) int 
返回实际拷贝的元素数量

我们在使用copy函数的时候要注意:两个切片的类型必须一致,否则会出现错误。当des切片的长度(len)小于src的长度(len)时(是长度而不是容量),只会拷贝des能接收的最大数量的元素。

func main() {
   //切片的声明,并不会为其分配内存空间
   var slice []int
   for i := 0; i < 10;i ++{
      slice = append(slice,i)
      fmt.Printf("len: %d \t cap: %d pointer:%p \n",len(slice),cap(slice),slice)
   }
   fmt.Print(slice)

   //make(切片类型,切片长度,切片容量)
   var copySlice = make([]int,4,5)
   //当des切片的长度(len)小于src的长度(len)时(是长度而不是容量),只会拷贝des能接收的最大数量的元素。
   copy(copySlice,slice)
   fmt.Println(copySlice)
}
--------
output:
[0 1 2 3 4 5 6 7 8 9][0 1 2 3]

切片中元素的删除

切片中并没有直接删除元素的方法,所以我们需要利用切片的特性来删除特定位置的元素。

//切片删除

seq := []int{1, 2, 3, 4, 5, 6, 7, 8}

//删除位置

index := 3

//通过append()函数来删除

seq = append(seq[:index], seq[index+1:]...)

fmt.Println(seq)

...语法糖在golang中的用法

1: 用于接受函数多个不确定参数

2: 将切片打散传递

-例如 seq = append(seq[:index], seq[index+1:]...) ,就是将index+1之后的元素打散一个个拼接到seq[:index]之后

Map

声明:

  • var name map[KeyType]ValueType这样声明之后map仅仅是一个指针,并没有为其分配对应的内存空间,只有在初始化map的时候才会分配内存空间。

初始化操作:

  • 使用make函数初始化:

    var m1 map[int]string
    m1 = make(map[int]string)
    
  • 声明时初始化:

    m2 := map[int]string{
       1:"11",
       2:"22",
    }
    

map的遍历

m2 := map[int]string{
   1:"11",
   2:"22",
}

//key和value一起打印
for key,value := range m2{
   fmt.Printf("key:%d value:%s \n",key,value)
}

//单独打印key
for key := range m2{
   fmt.Printf("key:%d \n", key)
}

//单独打印value
for _,value := range m2{
   fmt.Printf("value:%s \n", value)
}
-------
output:
key:1 value:11 
key:2 value:22 
key:1 
key:2 
value:11 
value:22 

delete函数

我们可以使用Go的内建函数delete()来删除map中的键值对,delete格式如下

delete(map,key)

该方法没有返回值

delete()函数只能删除指定的一个键值对,如果我们要清空map中的元素,唯一的方法就是重新make一个新的map

list

初始化:

  • 通过 container/list 包的 New() 函数初始化 list :变量名 := list.New()
  • 通过 var 关键字声明初始化 list : var 变量名 list.List

插入元素

方法功能
func (l *List) PushFront(v interface{}) *Element向列表头部添加元素,返回添加节点的指针
func (l *List) PushBack(v interface{}) *Element向列表尾部添加元素,返回添加节点的指针
func (l *List) InsertAfter(v interface{}, mark *Element) *Element在给定节点之后插入元素,返回插入节点的指针
func (l *List) InsertBefore(v interface{}, mark *Element) *Element在给定节点之前插入元素,返回插入节点的指针
func (l *List) PushBackList(other *List)将other列表添加到当前列表尾部
func (l *List) PushFrontList(other *List)将other列表添加到当前列表头部

例如:

listTest := list.New()
listTest.PushFront("123") //将字符串123插入列表头部
listTest.PushFront(123) //将数值123插入列表头部,列表为:123 -〉 “123

删除元素

1. package main
2. 
3. import "container/list"
4. 
5. func main() {
6.     l := list.New()
7. 
8.     // 尾部添加
9.     l.PushBack("canon")
10. 
11.     // 头部添加
12.     l.PushFront(67)
13. 
14.     // 尾部添加后保存元素句柄
15.     element := l.PushBack("fist")
16. 
17.     // 在fist之后添加high
18.     l.InsertAfter("high", element)
19. 
20.     // 在fist之前添加noon
21.     l.InsertBefore("noon", element)
22. 
23.     // 使用
24.     l.Remove(element)
25. }

代码说明如下:
第 6 行,创建列表实例。
第 9 行,将字符串 canon 插入到列表的尾部。
第 12 行,将数值 67 添加到列表的头部。
第 15 行,将字符串 fist 插入到列表的尾部,并将这个元素的内部结构保存到 element 变量中。
第 18 行,使用 element 变量,在 element 的位置后面插入 high 字符串。
第 21 行,使用 element 变量,在 element 的位置前面插入 noon 字符串。
第 24 行,移除 element 变量对应的元素。

遍历列表

遍历双链表需要配合 Front() 函数获取头元素,遍历时只要元素不为空就可以继续进行,每一次遍历都会调用元素的 Next() 函数,代码如下所示。

1.   l := list.New()
2. 
3.   // 尾部添加
4.   l.PushBack("canon")
5. 
6.   // 头部添加
7.   l.PushFront(67)
8. 
9.   for i := l.Front(); i != nil; i = i.Next() {
10.   fmt.Println(i.Value)
11.   }
------
output:
67
canon

代码说明如下:

  • 第 1 行,创建一个列表实例。
  • 第 4 行,将 canon 放入列表尾部。
  • 第 7 行,在队列头部放入 67。
  • 第 9 行,使用 for 语句进行遍历,其中 i:=l.Front() 表示初始赋值,只会在一开始执行一次,每次循环会进行一次 i != nil 语句判断,如果返回 false,表示退出循环,反之则会执行 i = i.Next()。
  • 第 10 行,使用遍历返回的 *list.Element 的 Value 成员取得放入列表时的原值。