Golang中从切片移除元素创建新切片会修改原切片和其他切片的问题

Golang中从切片移除元素创建新切片会修改原切片和其他切片的问题 你好,

我在从切片中移除元素时遇到了问题。本质上,我试图实现的是:获取一个基础元素切片,并创建多个新切片,每个新切片都从基础切片中移除了一个索引。以下是一个示例程序来说明这一点:

package main

import (
	"fmt"
)

func main() {
	base := []int{1, 2, 3, 4, 5}
	removal1 := RemoveElement(base, 0)
	removal2 := RemoveElement(base, 1)

	fmt.Printf("%v\n", base)
	fmt.Printf("%v\n", removal1)
	fmt.Printf("%v\n", removal2)
}

func RemoveElement(slice []int, index int) []int {
	return append(slice[:index], slice[index+1:]...)
}

我期望看到的结果是:

  • [1, 2, 3, 4, 5]
  • [2, 3, 4, 5]
  • [1, 3, 4, 5]

但我实际看到的结果却是:

  • [2, 4, 5, 5, 5]
  • [2, 4, 5, 5]
  • [2, 4, 5, 5]

我不知道我是否做错了什么,但我无法找出原因或如何修复它。任何帮助或想法都将不胜感激。

编辑

不知怎的,就在发帖之后,我想到使用一个新变量,像这样:

func RemoveElement(slice []int, index int) []int {
	newSlice := []int{}
	newSlice = append(newSlice, slice[:index]...)
	newSlice = append(newSlice, slice[index+1:]...)
	return newSlice
}

这似乎解决了问题,所以我假设我的原始代码以某种方式修改了原始切片,尽管我想尝试理解为什么,因为在我看来,这一切都应该是通过引用传递的。


更多关于Golang中从切片移除元素创建新切片会修改原切片和其他切片的问题的实战教程也可以访问 https://www.itying.com/category-94-b0.html

4 回复

由于你的基础切片没有触发扩容,你实际上是在修改同一个底层数组。append 会根据容量是否被超过来决定是否返回相同的底层数组。

更多关于Golang中从切片移除元素创建新切片会修改原切片和其他切片的问题的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


您可以简单地使用或复制标准库中 slices.Delete 函数的代码。

func main() {
    fmt.Println("hello world")
}

你好! 你正在使用并覆盖这个切片。 将 RemoveElement 函数替换为以下函数来进行调试:

func RemoveElement(slice []int, index int) []int {
	a := slice[:index]
	fmt.Printf("a: %p\n", a)
	b := slice[index+1:]
	fmt.Printf("b: %p\n", b)
	c := append(a, b...)
	fmt.Printf("c: %p\n", c)
	return c
}

在这种情况下,“a” 和 “c” 的地址是相同的。 你正在向指针的起始位置进行“追加”。

这是因为切片底层共享数组导致的修改问题。你的原始代码确实会修改原切片和其他切片。

package main

import (
	"fmt"
)

func main() {
	base := []int{1, 2, 3, 4, 5}
	removal1 := RemoveElement(base, 0)
	removal2 := RemoveElement(base, 1)

	fmt.Printf("base: %v\n", base)
	fmt.Printf("removal1: %v\n", removal1)
	fmt.Printf("removal2: %v\n", removal2)
}

func RemoveElement(slice []int, index int) []int {
	return append(slice[:index], slice[index+1:]...)
}

问题在于:

  1. slice[:index]slice[index+1:] 都是原切片的视图,共享底层数组
  2. append 操作会修改底层数组的内容
  3. 当移除索引0时,slice[:0] 是空切片,slice[1:][2,3,4,5],append 后底层数组变为 [2,3,4,5,5]
  4. 当移除索引1时,slice[:1][2]slice[2:][4,5,5],append 后底层数组变为 [2,4,5,5,5]

你的修复方案是正确的,创建新切片避免共享底层数组:

func RemoveElement(slice []int, index int) []int {
	newSlice := make([]int, 0, len(slice)-1)
	newSlice = append(newSlice, slice[:index]...)
	newSlice = append(newSlice, slice[index+1:]...)
	return newSlice
}

更简洁的写法:

func RemoveElement(slice []int, index int) []int {
	return append(append([]int{}, slice[:index]...), slice[index+1:]...)
}

或者使用 copy:

func RemoveElement(slice []int, index int) []int {
	result := make([]int, len(slice)-1)
	copy(result, slice[:index])
	copy(result[index:], slice[index+1:])
	return result
}

这些方法都会创建新的底层数组,避免修改原切片。

回到顶部