Golang中如何向切片的切片追加元素
Golang中如何向切片的切片追加元素 我目前正在编写第二个Go程序——第一个是"Hello World"的修改版,叫做"Hello Pete"——我遇到了切片的一个特性,对此我感到困惑。希望这里有人能好心帮忙解释一下。
我想要一次读取一个CSV文件的记录,提取字段的子集,并用它们来填充一个字符串切片的切片。我的问题在于这个过程的最后部分。
我尝试在这里和Playground中重现我所看到的行为。
package main
import (
"fmt"
)
func main() {
mySOS := make([][]string, 1)
fmt.Printf("mySliceOfSlices is of type %T and has value %v\n\n", mySOS, mySOS)
row0 := []string{"Twas", "brillig", "and", "the", "slithy", "toves"}
row1 := []string{"did", "gyre", "and", "gimble", "in", "the"}
row2 := []string{"wabe", "all", "mimsey", "were", "the", "borogoves"}
// row3 := []string{"and", "the", "mome", "raths", "outgrabe", "."}
s := make([]string, 3)
s[0] = row0[2]
s[1] = row0[3]
s[2] = row0[4]
fmt.Printf("s is of type %T and has value %v\n", s, s)
mySOS = append(mySOS, s)
fmt.Printf("mySliceOfSlices is of type %T and has value %v\n\n", mySOS, mySOS)
s[0] = row1[2]
s[1] = row1[3]
s[2] = row1[4]
fmt.Printf("s is of type %T and has value %v\n", s, s)
mySOS = append(mySOS, s)
fmt.Printf("mySliceOfSlices is of type %T and has value %v\n\n", mySOS, mySOS)
s[0] = row2[2]
s[1] = row2[3]
s[2] = row2[4]
fmt.Printf("s is of type %T and has value %v\n", s, s)
mySOS = append(mySOS, s)
fmt.Printf("mySliceOfSlices is of type %T and has value %v\n", mySOS, mySOS)
}
https://play.golang.org/p/o0ufVU3sBzR
我的困惑在于,mySOS最终总是完全填充了测试数据中的最后一个切片。在花了相当长的时间盯着它看之后,我尝试了一种稍微不同的方法,可以在这里看到。
package main
import (
"fmt"
)
func main() {
mySOS := make([][]string, 1)
fmt.Printf("mySliceOfSlices is of type %T and has value %v\n\n", mySOS, mySOS)
row0 := []string{"Twas", "brillig", "and", "the", "slithy", "toves"}
row1 := []string{"did", "gyre", "and", "gimble", "in", "the"}
row2 := []string{"wabe", "all", "mimsey", "were", "the", "borogoves"}
// row3 := []string{"and", "the", "mome", "raths", "outgrabe", "."}
/*
s := make([]string, 3)
s[0] = row0[2]
s[1] = row0[3]
s[2] = row0[4]
*/
// fmt.Printf("s is of type %T and has value %v\n", s, s)
mySOS = append(mySOS, row1[2:5])
fmt.Printf("mySliceOfSlices is of type %T and has value %v\n\n", mySOS, mySOS)
/*
s[0] = row1[2]
s[1] = row1[3]
s[2] = row1[4]
*/
// fmt.Printf("s is of type %T and has value %v\n", s, s)
mySOS = append(mySOS, row0[2:5])
fmt.Printf("mySliceOfSlices is of type %T and has value %v\n\n", mySOS, mySOS)
/*
s[0] = row2[2]
s[1] = row2[3]
s[2] = row2[4]
*/
// fmt.Printf("s is of type %T and has value %v\n", s, s)
mySOS = append(mySOS, row2[2:5])
fmt.Printf("mySliceOfSlices is of type %T and has value %v\n", mySOS, mySOS)
}
https://play.golang.org/p/nbWWEjHTzEX
这次mySOS包含了我期望的数据。但我仍然不明白为什么初始代码的结果会不同。
如前所述,如果有人能帮助澄清我明显的误解,我将不胜感激。
更多关于Golang中如何向切片的切片追加元素的实战教程也可以访问 https://www.itying.com/category-94-b0.html
啊,好的。那么我是否可以这样理解:它引用的是s(沿用第一个例子)——这个引用被附加到mySOS上,而不是值本身?
更多关于Golang中如何向切片的切片追加元素的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
明白了。非常感谢您的帮助,以及那篇写得很好且清晰易懂的文章链接。
切片"值"包含指向"底层数组"的指针。(参见《切片内部机制》https://blog.golang.org/go-slices-usage-and-internals)
因此,没错,您正在通过切片引用不断更新底层数组,并持续向其追加引用。
在你的第一个示例中,你不断重新定义切片 s[:3],导致它最终指向最后一组值。每个"s"切片都需要一个新的底层数组。请参阅:https://play.golang.org/p/Vz1b8afT_eJ
你遇到的问题是由于切片底层数组的共享导致的。在你的第一个代码示例中,你重复使用同一个切片 s 来追加到 mySOS 中,这导致所有追加的切片都指向同一个底层数组。
当你在第一个示例中这样做时:
s := make([]string, 3)
s[0] = row0[2]
s[1] = row0[3]
s[2] = row0[4]
mySOS = append(mySOS, s)
s[0] = row1[2] // 这会修改底层数组
s[1] = row1[3]
s[2] = row1[4]
mySOS = append(mySOS, s) // 再次追加同一个切片
每次修改 s 时,你实际上是在修改同一个底层数组,而 mySOS 中存储的是对这个切片的引用。因此,所有看起来不同的切片实际上都指向相同的内存位置。
正确的做法是每次创建一个新的切片:
package main
import (
"fmt"
)
func main() {
mySOS := make([][]string, 0) // 从空切片开始
row0 := []string{"Twas", "brillig", "and", "the", "slithy", "toves"}
row1 := []string{"did", "gyre", "and", "gimble", "in", "the"}
row2 := []string{"wabe", "all", "mimsey", "were", "the", "borogoves"}
// 方法1:每次创建新切片
s1 := []string{row0[2], row0[3], row0[4]}
mySOS = append(mySOS, s1)
s2 := []string{row1[2], row1[3], row1[4]}
mySOS = append(mySOS, s2)
s3 := []string{row2[2], row2[3], row2[4]}
mySOS = append(mySOS, s3)
fmt.Printf("mySliceOfSlices: %v\n", mySOS)
}
或者使用切片表达式(如你的第二个示例),这会创建新的切片:
package main
import (
"fmt"
)
func main() {
mySOS := make([][]string, 0)
row0 := []string{"Twas", "brillig", "and", "the", "slithy", "toves"}
row1 := []string{"did", "gyre", "and", "gimble", "in", "the"}
row2 := []string{"wabe", "all", "mimsey", "were", "the", "borogoves"}
mySOS = append(mySOS, row0[2:5])
mySOS = append(mySOS, row1[2:5])
mySOS = append(mySOS, row2[2:5])
fmt.Printf("mySliceOfSlices: %v\n", mySOS)
}
对于你的CSV处理场景,推荐的做法是:
package main
import (
"encoding/csv"
"fmt"
"strings"
)
func main() {
csvData := `Twas,brillig,and,the,slithy,toves
did,gyre,and,gimble,in,the
wabe,all,mimsey,were,the,borogoves`
reader := csv.NewReader(strings.NewReader(csvData))
records, _ := reader.ReadAll()
mySOS := make([][]string, 0)
for _, record := range records {
if len(record) >= 5 {
// 提取字段2-4(索引2,3,4)
subset := make([]string, 3)
copy(subset, record[2:5])
mySOS = append(mySOS, subset)
}
}
fmt.Printf("Processed data: %v\n", mySOS)
}
关键点在于理解切片是对底层数组的视图,重复使用同一个切片变量会导致所有引用都指向相同的数据。每次需要新的独立数据时,应该创建新的切片。

