Golang循环使用时结果仅保留最后一项的问题

Golang循环使用时结果仅保留最后一项的问题 大家好,

在下面的代码中,映射设置的值与我的预期不符。

我是否遗漏了什么?

package main

import (
	"fmt"
)

func main() {
	mySlice := []string{"1st", "2nd", "3rd"}
	fmt.Println(mySlice)
	
	newMap := make(map[string]*string)
	
	for key, item := range mySlice {
		switch currentItem := item; currentItem {
		case "1st":
			// newMap[string(key)] = &mySlice[key] // 设置我期望的值
			newMap[string(key)] = &item // 设置切片中的最后一个值

			fmt.Println("Added", currentItem)
		default:
			fmt.Println("Not added", currentItem)
		}
	}
	
	fmt.Print("\nResult using value:")
	for k, v := range newMap {
		fmt.Println(k, *v)
	}
}

https://play.golang.org/p/m7HzMDTi_CG


更多关于Golang循环使用时结果仅保留最后一项的问题的实战教程也可以访问 https://www.itying.com/category-94-b0.html

5 回复

似乎是切片表示和映射到底层内存地址时出现了问题。我不确定为什么它会返回第三个项目,除了你的映射是字符串指针而切片已经是一组指向底层数组的指针之外。如果你看这个没有映射字符串指针的代码示例,它按预期工作:https://play.golang.org/p/u9kJdZTAA8i

我希望这能帮助你确定问题所在,也许其他人可以更好地解释这种行为。

更多关于Golang循环使用时结果仅保留最后一项的问题的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


不要在循环内尝试获取范围循环中某些变量的地址。在范围循环中,每次迭代时元素的值都会被复制到给定的范围标识符中,而循环内部使用的就是这个副本。相反,应该使用传统的 for i := 0... 循环来有效获取数组/切片等结构的元素。

更多信息请参考:https://github.com/golang/go/wiki/Range

还有疑问。

我尝试在循环中重新赋值元素,然后发现无论内存空间如何,代码都能很好地运行。

	for key, item := range mySlice {
		item := item	// 为什么这样能正常工作???
		fmt.Println("Where is written: ", item.Value, &item.Value)

链接

但我很好奇为什么能正常工作,以及这是否安全。

在这种情况下,变量重新赋值是否足以成为解决方案?

你好,

遗憾的是,我使用的是结构体而不是字符串。

以下是与我的工作几乎相同的实际代码:

package main

import (
	"fmt"
)

type mySet struct {
	Indicator	string
	Value		string
}

func main() {
	mySlice := []mySet{
		mySet {
			Indicator: "new",
			Value: "1st-value",
		},
		mySet {
			Indicator: "change",
			Value: "2nd-value",
		},
		mySet {
			Indicator: "delete",
			Value: "3rd-value",
		},
	}
	fmt.Println(mySlice)
	
	newMap := make(map[string]*mySet)
	
	for key, item := range mySlice {
		fmt.Println("Where is written: ", item.Value, &item.Value)

		switch item.Indicator {
		// Problem is "new"
		case "new":
			newMap["0"] = &mySlice[key]	// set value which I expected
			// newMap["0"] = &item		// set value which last in slice

			fmt.Println("Added", item.Indicator, key)
		case "change":
			newMap["0"].Value = item.Value

			fmt.Println("Changed value of the new item", item.Indicator, key)
		default:
			fmt.Println("Not added", item.Indicator, key)
		}
	}
	
	fmt.Print("\nResult using value:")
	for k, v := range newMap {
		fmt.Println(k, v)
	}
}

https://play.golang.org/p/dU8HYZ3lO8q

也许这个结果是由循环使用的相同内存空间引起的。

感谢您的友好回答。

在Go语言中,循环变量在每次迭代中会被重用,而不是重新创建。这意味着item变量在循环过程中始终是同一个内存地址,只是每次迭代时被赋予新的值。

在你的代码中,&item获取的是循环变量item的地址,而不是切片中对应元素的地址。因此,所有映射值都指向同一个内存位置,最终保存的是最后一次迭代时的值。

以下是修正后的代码:

package main

import (
	"fmt"
)

func main() {
	mySlice := []string{"1st", "2nd", "3rd"}
	fmt.Println(mySlice)
	
	newMap := make(map[string]*string)
	
	for key, item := range mySlice {
		switch currentItem := item; currentItem {
		case "1st":
			// 正确的做法:获取切片元素的地址
			newMap[string(key)] = &mySlice[key]
			fmt.Println("Added", currentItem)
		default:
			fmt.Println("Not added", currentItem)
		}
	}
	
	fmt.Print("\nResult using value:")
	for k, v := range newMap {
		fmt.Println(k, *v)
	}
}

或者,为了避免指针操作带来的复杂性,可以使用值而不是指针:

package main

import (
	"fmt"
)

func main() {
	mySlice := []string{"1st", "2nd", "3rd"}
	fmt.Println(mySlice)
	
	newMap := make(map[string]string)
	
	for key, item := range mySlice {
		switch currentItem := item; currentItem {
		case "1st":
			newMap[string(key)] = item
			fmt.Println("Added", currentItem)
		default:
			fmt.Println("Not added", currentItem)
		}
	}
	
	fmt.Print("\nResult using value:")
	for k, v := range newMap {
		fmt.Println(k, v)
	}
}

关键点:在循环中,如果需要获取元素的地址,应该使用&mySlice[key]而不是&item,因为item是循环变量的副本,其地址在每次迭代中保持不变。

回到顶部