Go:附加,如果唯一

Kyl*_*ley 59 append go slice

有没有办法检查切片/贴图是否存在值?

我想在切片中存在切片时才向切片添加值.

这有效,但似乎很冗长.有没有更好的方法来做到这一点?

orgSlice := []int{1, 2, 3}
newSlice := []int{}
newInt := 2

newSlice = append(newSlice, newInt)
for _, v := range orgSlice {
    if v != newInt {
        newSlice = append(newSlice, v)
    }
}

newSlice == [2 1 3]
Run Code Online (Sandbox Code Playgroud)

tux*_*21b 84

每次插入时,您的方法都需要线性时间.更好的方法是使用a map[int]struct{}.或者,您也可以使用map[int]bool或类似的东西,但空struct{}的优点是它不占用任何额外的空间.因此map[int]struct{}是一组整数的流行选择.

例:

set := make(map[int]struct{})
set[1] = struct{}{}
set[2] = struct{}{}
set[1] = struct{}{}
// ...

for key := range(set) {
  fmt.Println(key)
}
// each value will be printed only once, in no particular order


// you can use the ,ok idiom to check for existing keys
if _, ok := set[1]; ok {
  fmt.Println("element found")
} else {
  fmt.Println("element not found")
}
Run Code Online (Sandbox Code Playgroud)

  • 我会在计算过程中只使用地图(因为它有O(1)行为而不是O(n)).之后,您可以创建切片并从地图中复制每个值.之后元素将具有随机顺序,因此您可能希望对其进行排序.您可以使用int,float,struct,string和数组作为映射键(至少在Go1中). (4认同)
  • 感谢你的回复。有几个问题:您将如何重新创建切片?有没有办法让这个策略与字符串一起工作?抱歉,如果这些很明显 - 我是 Go 新手。 (2认同)
  • 特别感谢您概述空结构不占用额外空间。我不知道这一点,并且会使用 map[type]interface{} 并将 nil 分配给接口。 (2认同)
  • 我也使用了map[type]interface{}方法,这不是也不需要额外的空间吗? (2认同)

Son*_*nia 34

如果你没有找到它,效率最高的可能是在切片上迭代并附加.

func AppendIfMissing(slice []int, i int) []int {
    for _, ele := range slice {
        if ele == i {
            return slice
        }
    }
    return append(slice, i)
}
Run Code Online (Sandbox Code Playgroud)

它简单明了,对于小型列表来说速度很快.

此外,它总是比您当前基于地图的解决方案更快.无论如何,基于地图的解决方案遍历整个切片; 当此解决方案发现新值已存在时立即返回.两种解决方案都会在迭代时比较元素.(每个地图赋值语句当然在内部至少进行一次地图键比较.)只有在可以跨多次插入维护地图时,地图才有用.如果您在每次插入时重建它,那么所有优势都将丢失.

如果您确实需要有效处理大型列表,请考虑按排序顺序维护列表.(我怀疑顺序对你没关系,因为你的第一个解决方案附加在列表的开头,最后的解决方案附加在最后.)如果你始终保持列表排序,那么你可以使用sort.Search函数来做有效的二进制插入.

  • "无论如何,基于地图的解决方案都会迭代整个切片" - 您确定这是散列图的工作方式吗? (13认同)
  • @FilipBartuzi实际上,我想我可能误解了这个陈述的含义.散列图显然不会迭代元素以找到关键字,但是"基于地图的解决方案""追加到切片是否唯一"会失去它的优势,如果我们必须将切片转换为贴图然后将贴图转换回切片. (5认同)