数组Array

声明数组

 声明数组的方式数组变量名在前,其次跟数组长度,最后定义数组的类型.同变量可以使用var声明也可以使用:=声明数组.需要注意的是通过:=声明的数组需要赋值

package main

import "fmt"

func main() {
    // 定义三个长度的字符串数组arr1
    var arr1 [3] string
    // 给arr1赋值
    arr1 = [3]string{"a","b","c"}
    // 修改arr1 第二个元素的值
    arr1[1] = "q"
    // 定义3个长度的字符串数组arr2,值为a,b,c
    arr2 := [3] string{"a", "b", "c"}
    // 定义任意长度的整形数组arr3,...意思是让系统自己判断有多少个长度
    arr3 := [...] int{1, 2, 3, 4, 5}
    fmt.Print(arr1, arr2, arr3)
}

  多维数组与一维数组声明一样,如下

package main

import "fmt"

func main() {
    // 声明二维数组,打印结果为[[0,0,0,0] [0,0,0,0]]
    var arr1 [2][4]int
    // 声明带参数的二维数组
    arr2 := [2][3]int{{1, 2, 3}, {4, 5, 6}}
    // 给第一组数组的第二个元素赋值为12
    arr2[0][1] = 12
    fmt.Print(arr1, arr2)
}

遍历数组

常规遍历

 通过数组的长度进行循环遍历

package main

import "fmt"

func main() {
    arr1 := [2][1]string{{"a"}, {"b"}}
    // 遍历最外层的数组
    for i := 0; i < len(arr1); i++ {
        // 遍历内层数组
        for k := 0; k < len(arr1[i]); k++ {
            fmt.Print(arr1[i][k])
        }
    }
}

range遍历

package main

import "fmt"

func main() {
arr := [...]int{1,2,3}
    // 获取健
    for key :=range arr{
        fmt.Printf("下标为:%d \n",key)
    }
    // 获取值
    for _,value := range arr{
        fmt.Printf("值为:%d \n",value)
    }
    // 获取健,值
    for k,v := range arr{
        fmt.Printf("下标为:%d,值为:%d \n",k,v)
    }
  
    // 二维数组的遍历
    arr1 := [2][1]string{{"a"}, {"b"}}
    // 获取最外围数组的下标k
    for k := range arr1{
        // 获取最内层数组的值,忽略下标.再一次循环
        for _,value :=  range arr1[k]{
            // 打印a,b
            fmt.Print(value)
        }
    }
}

类型

数组是值类型

  简单理解就是arr1[3] in tarr1[4] int是不同的类型,并且将数组传入到函数中,回进行拷贝.示例如下

package main

import "fmt"

// 传入任意长度int类型数组
func qvbilam(array [3]int){
    array[0] = 123
    fmt.Print(array)
}

func main() {
    arr1 := [3]int{1,1,1}
    arr2 := [3]int{2,2,2}
    // 打印结果为[123,1,1]
    qvbilam(arr1)
    // 打印结果为[123,2,2]
    qvbilam(arr2)
    // 打印结果[1,1,1]
    fmt.Print(arr1)
    // 打印结果[2,2,2]
    fmt.Print(arr2)
}

  可以看到将数组传入到函数中进行加工,在函数里的数组相当于是传入数组的拷贝,再对拷贝的数组进行加工后,原来的数组是不会变的.如果不想使用数组的拷贝可以使用指针来引用数组.示例如下

使用*作为数组的指针,调用函数传入的数组需要带上&

package main

import "fmt"

// 传入任意长度int类型数组
func qvbilam(array *[3]int){
    array[0] = 123
    fmt.Print(array)
}

func main() {
    arr1 := [3]int{1,1,1}
    arr2 := [3]int{2,2,2}
    // 打印结果为[123,1,1]
    qvbilam(&arr1)
    // 打印结果为[123,2,2]
    qvbilam(&arr2)
    // 打印结果[123,1,1]
    fmt.Print(arr1)
    // 打印结果[123,2,2]
    fmt.Print(arr2)
}

切片Slice

概念

  切片是数组连续片段的引用,可以是整个数组,也可以是数组的一部分,也可以是切片本身(小东西有点抽象啊).切片是由地址,大小,容量组成.

声明切片

直接声明切片

  使用var Name []Type声明切盘,[]什么都不写为切片,有值则为数组.

// 声明int类型切片
var slice []int
// 打印 结果[] 长度0
fmt.Print(slice,len(slice))

截取数组生成切片

  截取生成切片slice [start:stop].start是开始位置,stop是结束位置,截取个数=stop-start.

 // 声明切片
s := [5]string{"a", "b", "c", "d", "e"}
// 截取切片从第3个位置获取,获取5-2个元素
slice1 := s[2:5]
// 从第三个位置开始,知道结束
slice2 := s[2:]
// 从第一个位置开始,截取到3-0个元素
slice3 := s[:3]
// 截取全部
slice4 := s[:]
// 结果为: [c d e] [c d e] [a b c] [a b c d e]
fmt.Print(slice1, slice2, slice3, slice4)

重制切片

  正如前面所说,切片可以指向数组,也可以指向切片本身.这种也可以理解成切片的重制

func main() {
    // 生成数组
    arr := [5]int{0,1,2,3,4}
    // 生成切片[0,1,2,3]
    slice := arr[:4]
    // 输出[0 1 2 3]
    fmt.Print(slice)
    // 从切片中生成新的切片
    new_slice := slice[:3]
    // 输出[0 1 2]
    fmt.Print(new_slice)
    // 重制切片
    new_slice = new_slice[2:]
    // 输出[2]
    fmt.Print(new_slice)
}

make函数生成切片

  make()函数声明切片格式为make([]Type,Size,Cap).其中Type:类型,Size:分配元素个数,Cap预分配元素个数,减少多次分配空间造成性能的问题.

func main() {
    // size:2,cap:2
    sliceA := make([]int,2,2)
    // size:2,cap:5
    sliceB := make([]int,2,5)
    // 数据结果均为[0 0]
    fmt.Print(sliceA,sliceB)
    // 长度结果均为2
    fmt.Print(len(sliceA), len(sliceB))
}

类型

切片为引用类型

  数组是数值类型,而切片本身不是数组,但是它指向底层数组.并且为引用类型,声明切片指向数组,将切片传入到函数中进行修改,在函数外的原切片和数组都进行了修改.如下.

package main

import "fmt"

func myslice(slice []string){
    slice[2] = "ccc"
    // 输出结果 [a b ccc d e]
    fmt.Println("进入函数修改值后的slice:",slice)
}

func main() {
    // 声明切片
    s := [5]string{"a", "b", "c", "d", "e"}
    slice := s[:]
    myslice(slice)
    // 输出结果 [a b ccc d e]
    fmt.Print(s)
    // 输出结果 [a b ccc d e]
    fmt.Print("调用函数后的源切片slice:",slice)
}

拓展

  下例中的slice1指向数组[2:3],结果为[2 3],而slice2指向slice1的[1:3].也就是从slice1最后一个元素开始截取2个.其中一个元素是超出slice1的范围.结果还是能获得到[3 4].但是当slice3指向slice[1:4]则出现报错

func main() {
    // 生成数组
    arr := [5]int{0,1,2,3,4}
    // 截取数组的第三位开始的2个元素
    slice1 := arr[2:4]
    // 打印结果[2 3]
    fmt.Print(slice1)
  
    // 截取第一个切片第二个位置开始的2个元素
    slice2 := slice1[1:3]
    // 打印结果[3 4]
    fmt.Print(slice2)
  
    // 截取第一个切片第二个位置开始的3个元素
    slice3 := slice1[1:4]
    // 执行出现错误
        [2 3]panic: runtime error: slice bounds out of range
    fmt.Print(slice2)    
}

  切片是由ptr,len,cap组成,当切片截取数组.ptr为切片开头部分,len为切片的长度,cap为切片的开头到数组的结束.在不超过cap的情况下都可以向后进行拓展的.并且只可向后拓展,而不可以向前拓展.

拓展规则:slice[i]<=len(slice),向后拓展不可超越底层数组.

  如下slice1只截取了数组的[2],但是可以向后看到数组后的所有元素,也就是cap是[2,3,4].slice2截取的是slice[1:3].也就是slice1从第二个位置开始后截取2个元素,即[3,4].

func main() {
    // 生成数组
    arr := [5]int{0, 1, 2, 3, 4}
    slice1 := arr[2:3]
    slice2 := slice1[1:3]
    //slice1: [2]  len: 1  cap: 3
    fmt.Println("slice1:", slice1, " len:", len(slice1), " cap:", cap(slice1))
    //slice2: [3 4]  len: 2  cap: 2
    fmt.Println("slice2:", slice2, " len:", len(slice2), " cap:", cap(slice2))
}

复制

使用copy():将一个切片复制到另一个切片中,.返回值表示实际发生复制的元素个数

如两个切片不一样大,会按照其中较小的那个数组切片的元素个数进行复制

func main() {
    slice1 := []int{1, 2, 3, 4, 5}
    slice2 := []int{6,7,8}
      // 将slice2复制到slice1
    copy(slice1,slice2)
      // 输出结果[6 7 8 4 5]
    fmt.Print(slice1)
      
    slice3 := []int{1, 2, 3}
    slice4 := []int{6, 7, 8, 9, 10}
      // 将slice2复制到slice1
    copy(slice3, slice4)
    // 打印结果6,7,8
    fmt.Print(slice3)
}

追加

使用append():向切片人因位置动态添加元素.

func main() {
    slice := []int{}
    // 追加一个元素
    slice = append(slice,1)
    // 追加多个元素
    slice = append(slice,2,3,4,5)
    // 结果[1 2 3 4 5]
    fmt.Print(slice)
}

切片追加元素时,当空间不足,切片会生成新的切片以2倍数进行扩容

func main() {
    // 声明int类型切片
    slice := []int{}
    // 打印空切片len和cap
    fmt.Printf("len: %d, cap: %d \n",len(slice),cap(slice))
    for i := 0; i < 10; i++ {
        // 向切片追加自增变量i
        slice = append(slice,i)
        // 打印空切片len和cap
        fmt.Printf("len: %d, cap: %d \n",len(slice),cap(slice))
    }
}

输出结果 :可以看到当切片追加成功元素后,cap为1,再向后追加如果len>cap,则会生成新的切片.并且cap为原来切片的2倍.

len: 0, cap: 0 
len: 1, cap: 1 
len: 2, cap: 2 
len: 3, cap: 4 
len: 4, cap: 4 
len: 5, cap: 8 
len: 6, cap: 8 
len: 7, cap: 8 
len: 8, cap: 8 
len: 9, cap: 16 
len: 10, cap: 16 

删除

  没有删除函数直接对切片的元素进行删除.

截取删除

func printslice(slice []int) {
    fmt.Printf("slice: %d, len: %d, cap: %d \n", slice, len(slice), cap(slice))
}

func main() {
    slice := []int{1, 2, 3, 4, 5}
    // 删除第一个元素
    slice = slice[1:]
    printslice(slice)
  
    // 删除中间元素3
    slice = []int{1,2,4,5}

    // 删除最后一个元素
    slice = slice[:len(slice)-1]
    printslice(slice)
}

append函数删除

  原理将要被删除元素之前的元素与被删除元素之后的所有元素进行拼接.因为append()的第二传参是elems类型,在go中使用...即可追加切片后的所有.如下

func printslice(slice []int){
    fmt.Printf("slice: %d, len: %d, cap: %d \n",slice,len(slice),cap(slice))
}

func main() {

    slice := []int{1,2,3,4,5}
    // 删除第中间元素3.
    slice = append(slice[:2],slice[3:]...)
    printslice(slice)
  
    // 删除最后一个元素
    slice = slice[:len(slice)-1]
    printslice(slice)
}

copy函数删除

func printSlice(slice []int) {
    fmt.Printf("slice: %d, len: %d, cap: %d \n", slice, len(slice), cap(slice))
}

func main() {
    slice := []int{1,2,3,4,5}
    // 删除第一个元素
    slice1 := slice[:copy(slice,slice[1:])]
    printSlice(slice1)

    // 删除最后一个元素
    slice = []int{1,2,3,4,5}
    slice4 := slice[:copy(slice,slice[:4])]
    printSlice(slice4)
}

从中间位置删除:首先要明确copy()函数的返回值是实际复制的数量,在copy的同时也改变了底层数组.

func main() {
    slice := []int{1,2,3,4,5}
    // 以删除下标为2元素3为例
    slice2 := slice[2:] // {3,4,5}
    slice3 := slice[2+1:] // {4,5}
    // 合并后slice = {1,2,4,5,5}, copy返回值为2
    num := copy(slice2,slice3)
    fmt.Println(slice,num)
    // 截取4个,则为2+2,即截取下标+copy结果
    printSlice(slice[:2+2])
      // slice: [1 2 4 5], len: 4, cap: 5
}

总结

// k为要删除元素的下标,N为删除个数
slice = slice[:K+copy(slice[K]:],slice[K+N:])]

映射Map

  map是一种无序集合,也可以成为关联字典,因为存在着key,value.map是会自动增长的,不需要声明长度.

声明映射

通过var声明的空映射类型为nil

func main() {
    var mp3 map[string]string
    mp4 := map[string]string{}
    mp5 := make(map[string]string)
    // mp4 key:a value:A
    mp4["a"] = "A"
    fmt.Println(mp3, mp4, mp5)
}

map的值也可以是切片,但是在声明的时候需要使用make

func printMap(mp map[int][]int) {
    fmt.Println(mp)
}

func main() {
    mp := make(map[int][]int)
    mp[1] = []int{3, 4, 5}
    printMap(mp)
}
// 输出结果
map[1:[3 4 5]]

操作

遍历Map

func main() {
    mp := map[string]string{
        "sb":    "zcx",
        "angel": "gy",
    }
    for key,value :=range mp{
        fmt.Printf("key:%s, value:%s \n",key,value)
    }
}
// 结果
key:sb, value:zcx 
key:angel, value:gy 

获取值

func main() {
    mp := map[string]string{
        "sb":    "zcx",
        "angel": "gy",
    }
    sb := mp["sb"]
    fmt.Println(sb)
}

通过不存在的健获取值的结果不会报错,打印结果为空,判断key是否存在使用第二个变量接受map值

func main() {
    mp := map[string]string{
        "sb":    "zcx",
        "angel": "gy",
    }
    sb := "不存在key";
    if value, key := mp["sbb"]; key {
        // 因为在if中声明的k,v所以不能在if外部调用,需要再外部创建变量,成立再赋值
        sb = value
    }
    fmt.Println(sb)
}

删除值

  通过delete()函数进指定key进行删除

func main() {
    mp := map[string]string{
        "sb":    "zcx",
        "angel": "gy",
    }
    if _, key := mp["sb"]; key {
        delete(mp,"sb")
    }
    fmt.Println(mp)
}

列表List

  列表是一种非连续的存储容器,每个几点之间会通过变量记录下一个/上一个节点的位置.如单链表和双链表.

声明列表

func main() {
    var movie list.List
    language := list.New()
    // 获取类型
    movieType := reflect.TypeOf(movie)
    // 获取类型
    languageType := reflect.TypeOf(language)
    // 打印结果
    fmt.Println(movieType, languageType)
}

插入元素

如需要追加列表,函数带上List,例如PushBackList

操作说明
PushBack在末尾追加元素
PushFront在开头追加元素
InsertAfter在指定节的后面追加元素
InsertBefore在指定节点的后面追加
func main() {
    language := list.New()
    // 向列表尾部添加元素
    php := language.PushBack("PHP")
    // 向列表头部添加元素
    golang := language.PushFront("GoLang")
    // 向php元素后添加元素
    language.InsertAfter("python", php)
    // 向golang元素添加变量
    language.InsertBefore("c++", golang)
    for i := language.Front(); i != nil; i = i.Next() {
        // 打印结果: c++,GoLang,PHP,python,
        fmt.Print(i.Value,",")
    }
}

删除元素

  同插入中的Insert需要用节点记录对象的位置.然后使用Remove(Elemnt)删除元素,例如

func main() {
    language := list.New()
    // 向列表尾部添加元素
    php := language.PushBack("PHP")
    // 向列表头部添加元素
    golang := language.PushFront("GoLang")
    // 向php元素后添加元素
    language.InsertAfter("python", php)
    // 向golang元素添加变量
    language.InsertBefore("c++", golang)
    // 删除元素
    language.Remove(php)
    for i := language.Front(); i != nil; i = i.Next() {
        // 打印结果: c++,GoLang,python,
        fmt.Print(i.Value, ",")
    }
}

遍历所有元素

操作说明
Front()获取列表头元素
Next()获取下一个元素/没有了则返回nil

使用

func main() {
    language := list.New()
    // 向列表尾部添加元素
    php := language.PushBack("PHP")
    // 向列表头部添加元素
    golang := language.PushFront("GoLang")
    // 向php元素后添加元素
    language.InsertAfter("python", php)
    // 向golang元素添加变量
    language.InsertBefore("c++", golang)
    // 删除元素
    language.Remove(php)
    for i := language.Front(); i != nil; i = i.Next() {
        // 打印结果: c++,GoLang,python,
        fmt.Print(i.Value, ",")
    }
}
Last modification:March 23rd, 2020 at 05:01 pm