Go语言中的数组和切片是常用的集合类型,它们在处理数据时提供了不同的特性和灵活性。本文将深入探讨数组和切片的使用方法、特性、实例以及应用场景。

1. 数组和切片简介

在Go语言中,数组和切片都是集合类的类型,用于存储相同类型的元素。它们的主要区别在于数组的长度是固定的,而切片的长度是可变的。

1.1 数组

数组的长度在声明时就必须给定,并且在之后不会再改变。数组的长度是其类型的一部分,例如 [1]string[2]string 是两个不同的数组类型。

// 示例1:声明一个长度为5的整型数组
arr := [5]int{1, 2, 3, 4, 5}
1.2 切片

切片是数组的一层简单封装,切片的长度可以随着元素数量的增长而增长,但不会随着元素数量的减少而减少。切片的类型字面量中只有元素的类型,没有长度。

// 示例2:创建一个切片
slice := make([]int, 0, 5) // 初始长度为0,容量为5的整型切片

2. 切片表达式和容量

通过切片表达式,我们可以创建新的切片,指定切片的起始索引和结束索引。切片的容量表示底层数组中可以被切片窗口最右边元素的索引所覆盖的范围。

// 示例3:使用切片表达式创建切片
arr := []int{1, 2, 3, 4, 5, 6, 7, 8}
slice := arr[3:6] // 从索引3到索引6(不包括)创建切片

3. 切片的容量增长规律

切片在容量不足时会进行扩容,一般情况下新容量是原容量的两倍,但当原长度大于或等于1024时,新容量基准为原容量的1.25倍,直至满足新长度。

// 示例4:切片扩容规律演示
package main

import "fmt"

func main() {
    // 创建一个切片
    slice := make([]int, 0, 1)

    // 模拟切片扩容过程
    for i := 1; i <= 2048; i++ {
        slice = append(slice, i)
        fmt.Printf("Length: %d, Capacity: %d\n", len(slice), cap(slice))
    }
}

4. 切片的底层数组和追加元素

切片的底层数组永远不会被替换,扩容时会生成新的底层数组和新的切片。使用append函数对切片追加元素时,如果新长度不超过原容量,底层数组保持不变。

// 示例5:使用append函数追加元素
slice := []int{1, 2, 3}
slice = append(slice, 4, 5) // 追加元素4和5到切片末尾

5. 应用场景

  • 数组:适合用于表示固定长度的数据集合,如一周的天数 [7]string

    daysOfWeek := [7]string{"Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"}
    
  • 切片:常用于动态增长的数据集合,如日志收集、HTTP请求的处理等。

    logEntries := []string{"entry1", "entry2", "entry3"}
    
  • 切片扩容:当需要动态管理数据集合大小时,切片的自动扩容特性尤为重要,如读取文件内容、处理大量数据等场景。

    data := make([]byte, 0, 1024)
    // 读取文件内容并追加到切片
    for {
        chunk := readFromFile()
        if chunk == nil {
            break
        }
        data = append(data, chunk...)
    }
    

6. 进销存示例

package main

import "fmt"

func main() {
    // 声明一个存储商品名称的数组
    var products [3]string

    // 添加商品名称到数组中
    products[0] = "手机"
    products[1] = "电脑"
    products[2] = "平板"

    // 声明一个切片用于存储每个商品的库存数量
    var inventory []int

    // 初始化库存数量
    inventory = append(inventory, 10) // 手机库存数量
    inventory = append(inventory, 20) // 电脑库存数量
    inventory = append(inventory, 15) // 平板库存数量

    // 显示每个商品的名称和库存数量
    for i, product := range products {
        fmt.Printf("商品名称: %s, 库存数量: %d\n", product, inventory[i])
    }
}

以上代码是一个简单的 Go 语言程序,用于模拟一个简单的进销存系统。让我们逐行解释这段代码:

  1. package main:声明了这个文件属于 main 包,这是 Go 语言程序的入口包。

  2. import "fmt":导入了 fmt 包,这个包实现了格式化输入输出的功能,我们可以使用它来打印输出信息到控制台。

  3. func main() { ... }:定义了程序的主函数,是程序执行的入口点。

  4. var products [3]string:声明了一个长度为 3 的字符串数组,用于存储商品名称。数组的长度是固定的,并且初始化为零值(空字符串)。

  5. products[0] = "手机"products[1] = "电脑"products[2] = "平板":将商品名称分别赋值给数组中的每个元素。

  6. var inventory []int:声明了一个整型切片,用于存储每个商品的库存数量。切片的长度可以动态增长,初始时长度为 0。

  7. inventory = append(inventory, 10)inventory = append(inventory, 20)inventory = append(inventory, 15):使用 append 函数向切片中追加元素,分别表示手机、电脑和平板的库存数量。

  8. for i, product := range products { ... }:使用 range 关键字遍历数组 productsi 表示当前索引,product 表示当前商品名称。在循环中,通过索引 i 可以从切片 inventory 中获取对应商品的库存数量,然后将商品名称和库存数量打印输出到控制台。

这段代码演示了如何使用数组和切片来实现一个简单的进销存系统,通过循环遍历打印出每个商品的名称和对应的库存数量。

7. 总结

  • 数组长度固定,切片长度可变。
  • 切片通过窗口查看底层数组,容量为切片窗口最右边元素的索引。
  • 切片扩容规律:一般为原容量的两倍,但长度大于等于1024时为1.25倍。
  • 切片的底层数组不会被替换,append返回新切片。
  • 数组适用于固定长度的集合,切片适用于动态管理数据集合。
03-17 06:10