ZetCode

Golang slices.Compact

最后修改于 2025 年 4 月 20 日

本教程将解释如何在 Go 中使用 slices.Compact 函数。我们将通过实际示例介绍删除重复项的切片操作。

slices.Compact 函数用单个副本替换连续相同的元素。它是 Go 实验性 slices 包的一部分。

此函数通过删除相邻的重复项来清理数据,非常有用。它会就地修改切片并返回切片的长度。

基本的 slices.Compact 示例

slices.Compact 最简单的用法是删除整数切片中的连续重复项。该函数会修改原始切片。

basic_compact.go
package main

import (
    "fmt"
    "slices"
)

func main() {
    numbers := []int{1, 1, 2, 2, 3, 3, 3, 4}
    
    newLength := slices.Compact(numbers)
    numbers = numbers[:newLength]
    
    fmt.Println("Compacted numbers:", numbers)
}

我们创建一个包含连续重复项的切片。压缩后,只剩下唯一的连续值。切片会被缩短到新的长度。

处理字符串

slices.Compact 也适用于字符串切片。此示例从切片中删除连续重复的单词。

string_compact.go
package main

import (
    "fmt"
    "slices"
)

func main() {
    words := []string{"hello", "hello", "world", "go", "go", "go"}
    
    newLength := slices.Compact(words)
    words = words[:newLength]
    
    fmt.Println("Compacted words:", words)
}

该函数比较字符串并删除连续的重复项。请注意,非连续的重复项仍然保留在切片中。

自定义比较函数

slices.CompactFunc 允许自定义比较逻辑。此示例压缩大小写不敏感的字符串重复项。

custom_compact.go
package main

import (
    "fmt"
    "slices"
    "strings"
)

func main() {
    words := []string{"Hello", "hello", "World", "world", "Go"}
    
    newLength := slices.CompactFunc(words, func(a, b string) bool {
        return strings.EqualFold(a, b)
    })
    words = words[:newLength]
    
    fmt.Println("Case-insensitive compact:", words)
}

我们使用 strings.EqualFold 进行大小写不敏感的比较。该函数会删除连续匹配但忽略大小写的单词。

使用结构体

我们可以压缩自定义结构体类型的切片。此示例删除连续具有相同年龄的人。

struct_compact.go
package main

import (
    "fmt"
    "slices"
)

type Person struct {
    Name string
    Age  int
}

func main() {
    people := []Person{
        {"Alice", 25},
        {"Bob", 25},
        {"Charlie", 30},
        {"Dave", 30},
        {"Eve", 30},
    }
    
    newLength := slices.CompactFunc(people, func(a, b Person) bool {
        return a.Age == b.Age
    })
    people = people[:newLength]
    
    fmt.Println("Compacted people:", people)
}

自定义比较函数仅检查 Age 字段。将删除连续具有相同年龄的人,只保留第一个出现的。

空切片行为

slices.Compact 可以很好地处理空切片。此示例演示了其对空切片和 nil 切片行为。

empty_slice.go
package main

import (
    "fmt"
    "slices"
)

func main() {
    var empty []int
    nilSlice := []string(nil)
    
    emptyLen := slices.Compact(empty)
    nilLen := slices.Compact(nilSlice)
    
    fmt.Println("Empty slice length:", emptyLen)
    fmt.Println("Nil slice length:", nilLen)
}

空切片和 nil 切片都返回长度 0。该函数可以安全地处理这些边缘情况而无需修改。

性能注意事项

对于大型切片,压缩可以提高内存效率。此示例对大型切片上的操作进行基准测试。

performance.go
package main

import (
    "fmt"
    "slices"
    "time"
)

func main() {
    largeSlice := make([]int, 1_000_000)
    for i := range largeSlice {
        largeSlice[i] = i % 10 // Creates many consecutive duplicates
    }
    
    start := time.Now()
    newLength := slices.Compact(largeSlice)
    largeSlice = largeSlice[:newLength]
    
    fmt.Println("Compacted length:", newLength)
    fmt.Println("Time taken:", time.Since(start))
}

该操作很高效,因为它是在原地操作的。内存使用量与输入大小无关,只修改现有切片。

实际示例:日志去重

此实际示例使用 slices.Compact 删除连续重复的日志条目,同时保留顺序。

log_deduplication.go
package main

import (
    "fmt"
    "slices"
)

func main() {
    logs := []string{
        "ERROR: Disk full",
        "ERROR: Disk full",
        "INFO: Backup started",
        "WARNING: High CPU usage",
        "WARNING: High CPU usage",
        "WARNING: High CPU usage",
        "INFO: Backup completed",
    }
    
    newLength := slices.Compact(logs)
    logs = logs[:newLength]
    
    fmt.Println("Deduplicated logs:")
    for _, log := range logs {
        fmt.Println(log)
    }
}

连续重复的日志消息被删除,同时保留原始顺序。这有助于减少日志噪声,同时保留重要序列。

来源

Go 实验性切片包文档

本教程介绍了 Go 中的 slices.Compact 函数,并通过实际示例说明了在各种场景下从切片中删除连续重复项的方法。

作者

我的名字是 Jan Bodnar,我是一名充满激情的程序员,拥有丰富的编程经验。自 2007 年以来,我一直在撰写编程文章。至今,我已撰写了 1,400 多篇文章和 8 本电子书。我在编程教学方面拥有十多年的经验。

列出所有 Go 教程