一、数组

数组是一种常见的数据结构,它可以用来存储多个相同类型的数据元素。数组中的元素通常按照一定的顺序进行排列,可以通过索引来访问和操作数组中的元素。在编程中,数组可以用于存储各种类型的数据,如数字、字符、对象等。

例如,我们可以定义一个整数数组来存储学生的成绩,或者定义一个字符串数组来存储学生的姓名。通过数组的索引,我们可以方便地访问修改数组中的元素。

package main
import "fmt"
// 定义一个学生成绩数组
var scores [5]int
func main() {
    // 给数组赋值
    scores[0] = 85
    scores[1] = 92
    scores[2] = 78
    scores[3] = 90
    scores[4] = 88
    // 输出数组元素
    for i, score := range scores {
        fmt.Printf("学生 %d 的成绩是: %d\n", i+1, score)
    }
}

二、切片

切片是一种动态数组,它可以根据需要增长或缩小。与数组不同的是,切片不需要预先指定长度,而是可以在运行时根据需要进行修改。切片通过索引和长度来访问和操作其中的元素。

例如,我们可以使用切片来存储学生的成绩列表,并且可以根据学生人数的变化动态地调整切片的长度。切片还提供了一些方便的操作,如追加元素、删除元素、截取子切片等。

以下是一个使用 Go 语言操作切片的示例:

package main
import "fmt"
// 定义一个切片
var scores []int
func main() {
    // 追加元素到切片
    scores = append(scores, 85)
    scores = append(scores, 92)
    scores = append(scores, 78)
    scores = append(scores, 90)
    scores = append(scores, 88)
    // 输出切片元素
    for i, score := range scores {
        fmt.Printf("学生 %d 的成绩是: %d\n", i+1, score)
    }
}

2.1 切片删除

在 Go 语言中,可以使用切片的一些方法来删除切片中的元素。例如,可以使用copy函数将切片的一部分复制到一个新的切片中,从而实现删除操作。

[1:2]使用规则是左闭右开

以下是一个示例,展示了如何删除切片中的特定元素:

package main

import (
    "fmt"
)

// 定义一个切片
var scores = []int{85, 92, 78, 90, 88}

func main() {
    // 删除索引为 2 的元素
    scores = deleteScore(scores, 2)
    // 输出切片元素
    fmt.Println(scores)
    for i, score := range scores {
        fmt.Printf("学生 %d 的成绩是: %d\n", i+1, score)
    }
}

func deleteScore(scores []int, index int) []int {
    if index < 0 || index >= len(scores) {
        // 如果索引不合法,直接返回原切片
        return scores
    }
    // 使用 append 方法删除索引为 index 的元素
    return append(scores[:index], scores[index+1:]...)
}

// 输出: 
// 学生 1 的成绩是: 85
// 学生 2 的成绩是: 92
// 学生 3 的成绩是: 90
// 学生 4 的成绩是: 88

在上述示例中,定义了一个名为deleteScore的函数,它接受一个切片和要删除的元素索引。在函数内部,创建了一个新的切片,长度比原切片少 1,并将原切片的一部分复制到新切片中,跳过要删除的元素。最后,将新切片复制回原切片中,实现了元素的删除。在main函数中,调用deleteScore函数删除索引为 2 的元素,并输出删除后的切片元素。

2.2 切片截取

Go的切片截取采用左闭右开的原则,闭[ 代表包含这个元素,开) 表示不包含这个元素

  1. arr[start:end],获取的是[start,end)之间的元素

  2. arr[:end],获取的是[0,end)之间的元素

  3. arr[start:],获取的是[start,len(arr))之间的元素

切片截取是一种常见的操作,它允许我们从原始切片中获取子切片。通过指定起始索引和结束索引(不包括结束索引),可以获取指定范围内的元素。

以下是一个示例,展示了如何进行切片截取:

package main
import "fmt"

// 定义一个切片
var scores = []int{90, 70, 50, 80, 60}

func main() {
    // 截取子切片
    subScores := scores[1:3]
    fmt.Println("子切片:", subScores) // 子切片: [70 50]

    // 截取子切片1
    subScores1 := scores[:3]
    fmt.Println("子切片1:", subScores1) // 子切片2: [90 70 50]

    // 截取子切片2
    subScores2 := scores[2:]
    fmt.Println("子切片2:", subScores2) // 子切片1: [50 80 60]
}

在上面的示例中,我们从原始切片scores中截取了子切片subScores,其范围是从索引 1 到索引 3(不包括索引 3)。输出结果将显示子切片中的元素。

2.3 切片排序

切片的排序可以通过标准库中的sort函数来实现。sort函数可以对切片进行升序或降序排序。

以下是一个示例,展示了如何对切片进行排序:

package main

import (
    "fmt"
    "sort"
)

// 定义一个切片
var scores = []int{50, 80, 30, 60, 90}

func main() {
    // 对切片进行排序
    sort.Ints(scores)
    // 输出排序后的切片
    for i, score := range scores {
        fmt.Printf("学生 %d 的成绩是: %d\n", i+1, score)
    }
}

// 学生 1 的成绩是: 30
// 学生 2 的成绩是: 50
// 学生 3 的成绩是: 60
// 学生 4 的成绩是: 80
// 学生 5 的成绩是: 90

2.4 切片的合并

切片的合并可以通过将多个切片连接在一起来实现。可以使用...操作符将一个切片展开为多个元素,然后将它们添加到另一个切片中。

以下是一个示例,展示了如何合并两个切片:

package main
import "fmt"
// 定义两个切片
var slice1 = []int{10, 20, 30}
var slice2 = []int{40, 50, 60}
func main() {
    // 合并两个切片
    slice3 := append(slice1, slice2...)
    fmt.Println("合并后的切片:", slice3) // [10 20 30 40 50 60]
}

在上面的示例中,使用append函数将slice1slice2合并到slice3中。通过...操作符将slice2展开为多个元素,然后将它们添加到slice1的后面。最后,输出合并后的切片slice3的内容。

2.5 判断某个元素是否在切片中

要判断某个元素是否在切片中,可以通过遍历切片的方式来实现。以下是一个示例代码:

package main

import (
    "fmt"
)

// contains 检查切片中是否包含指定的元素
func contains[T comparable](slice []T, element T) bool {
    for _, v := range slice {
        if v == element {
            return true
        }
    }
    return false
}

func main() {
    intSlice := []int{1, 2, 3, 4, 5}
    stringSlice := []string{"apple", "banana", "cherry"}

    // 检查整数切片
    intElement := 3
    if contains(intSlice, intElement) {
        fmt.Printf("整数切片包含元素 %d\n", intElement)
    } else {
        fmt.Printf("整数切片不包含元素 %d\n", intElement)
    }

    // 检查字符串切片
    stringElement := "banana"
    if contains(stringSlice, stringElement) {
        fmt.Printf("字符串切片包含元素 %s\n", stringElement)
    } else {
        fmt.Printf("字符串切片不包含元素 %s\n", stringElement)
    }
}

在上述代码中,定义了一个通用的contains函数,它可以处理任何可比较类型的切片。通过遍历切片中的每个元素,与给定的元素进行比较,如果找到了匹配项就返回true,遍历结束都未找到则返回false

main函数中,分别对整数切片和字符串切片进行了元素是否存在的判断,并根据结果输出相应的信息。这种通过自定义函数来检测元素是否在切片中的方法,提高了代码的复用性和可读性。

2.6 切片去重

实现切片去重可以通过多种方式,下面是一种常见的方法:

package main
import "fmt"
// unique 对切片进行去重操作
func unique(slice []int) []int {
    result := []int{}
    seen := make(map[int]bool)
    for _, num := range slice {
        if!seen[num] {
            result = append(result, num)
            seen[num] = true
        }
    }
    return result
}
func main() {
    slice := []int{1, 2, 2, 3, 3, 3, 4, 4, 4, 4}
    uniqueSlice := unique(slice)
    fmt.Println("去重后的切片:", uniqueSlice)
}

在上述代码中,unique函数使用一个map来记录已经出现过的元素。遍历输入的切片时,如果某个元素还未在map中出现,就将其添加到结果切片和map中。这种方式能够有效地去除切片中的重复元素,并保持原始元素的顺序。

main函数中,给出了一个包含重复元素的整数切片,并调用unique函数对其进行去重操作,最后打印出去重后的切片。这种去重方法在实际编程中经常使用,对于处理需要消除重复元素的数据非常有帮助。

2.7 切片扩容

切片扩容:重新分配一段连续内存,而后把原有的数据拷贝过去

在Go语言中,当切片的容量<1024时,切片会翻倍,>1024时,切片每次会增加约1/4

坑点:

package main

import "fmt"

func main() {
    // 原始切片
    s1 := make([]int, 0, 2)
    s1 = append(s1, 1, 2) // 此时s1已满 (len=2, cap=2)

    // 让s2成为s1的一个“视图”
    s2 := s1
    fmt.Printf("初始状态: s1: %v, ptr: %p | s2: %v, ptr: %p\n", s1, s1, s2, s2)

    // 对s1进行append,触发扩容
    s1 = append(s1, 3)
    fmt.Printf("扩容后: s1: %v, ptr: %p | s2: %v, ptr: %p\n", s1, s1, s2, s2)

    // 修改s1的第一个元素
    s1[0] = 100
    fmt.Printf("修改s1后: s1: %v | s2: %v\n", s1, s2)

    // 输出结果:
    // 初始状态: s1: [1 2], ptr: 0xc000018050 | s2: [1 2], ptr: 0xc000018050
    // 扩容后: s1: [1 2 3], ptr: 0xc00001c080 | s2: [1 2], ptr: 0xc000018050
    // 修改s1后: s1: [100 2 3] | s2: [1 2]
}
  1. s1s2最初指向同一块底层数组。

  2. s1进行append导致扩容,系统为s1重新分配了一块新内存,并将[1, 2, 3]拷贝过去。此时,s1指向了新地址。

  3. s2仍然指向旧的底层数组,其内容仍然是[1, 2]

  4. 之后对s1的修改,完全不会影响到s2

三、Map

Map 是 Go 语言中的一种无序键值对数据结构。它用于存储键值对,可以通过键来快速访问对应的值。Map 中的键可以是任何类型,而值可以是任何类型。

Go 语言中的 map 是引用类型,必须初始化才能使用

初始化dd := make(map[string]string)

以下是一个创建和使用 Map 的示例:

package main
import "fmt"
func main() {
    // 创建一个空的 Map
    studentGrades := make(map[string]int)
    // 向 Map 中添加键值对
    studentGrades["Alice"] = 85
    studentGrades["Bob"] = 90
    studentGrades["Charlie"] = 75
    // 通过键获取对应的值
    gradeForAlice := studentGrades["Alice"]
    fmt.Println("Alice 的成绩是:", gradeForAlice) // Alice 的成绩是: 85
    // 检查键是否存在于 Map 中
    exists := studentGrades["David"]
    fmt.Println("David 是否有成绩:", exists) // David 是否有成绩: 0
}

在上面的示例中,我们创建了一个名为studentGrades的空 Map。然后,使用键值对的方式向 Map 中添加数据。通过键"Alice"可以获取到对应的值 85。最后,我们检查键"David"是否存在于 Map 中,并输出结果。

3.1 判断某个键是否存在

可以通过访问特定键对应的值来判断该键是否存在于 Map 中。如果值为nil,则表示键不存在;如果值不为nil,则表示键存在。

以下是一个示例:

package main

import "fmt"

func main() {
  userInfo := map[string]string{"username": "zhangsan", "password": "123456"}
  v, ok := userInfo["username"]
  if ok {
    fmt.Println(v) // zhangsan
  } else {
    fmt.Println("map中没有此元素")
  }
}

3.2 Map的删除delete()函数

要从 Map 中删除一个键值对,可以使用 delete 函数。delete 函数接受一个 Map 和一个键作为参数,并删除与该键相关联的值。

以下是一个示例:

package main

import "fmt"

func main() {
  userInfo := map[string]string{"username": "root", "password": "123456"}
  delete(userInfo, "password") //将 password从 map 中删除
  fmt.Println(userInfo)        // map[username:root]
}

Map 是 Go 语言中非常强大和实用的数据结构,它能够高效地存储和操作键值对数据。通过灵活运用判断键是否存在、删除键值对等操作,我们可以根据不同的业务需求来对 Map 进行定制化的处理。

比如在一个用户管理系统中,我们可以使用 Map 来存储用户的各种信息。当需要更新或删除某个用户的特定信息时,上述的方法就派上了用场。

假设我们有一个电商系统,商品的库存信息也可以用 Map 来存储。键可以是商品的 ID,值则是对应的库存数量。当商品被售出或者补充库存时,就可以方便地对 Map 进行相应的修改和操作。

四、make函数

make函数用于创建切片、映射和通道。它可以指定这些数据结构的初始容量。例如,我们可以使用make函数创建一个具有特定容量的切片。

以下是一个使用make函数创建切片的示例:

package main
import "fmt"
func main() {
    // 使用 make 函数创建一个容量为 10 的整数切片
    scores := make([]int, 10)
    // 填充切片
    for i := 0; i < 10; i++ {
        scores[i] = i * 5
    }
    // 输出切片元素
    for i, score := range scores {
        fmt.Printf("元素 %d: %d\n", i+1, score)
    }
}