76

I really like google golang but could some one explain what the rationale is for the implementors having left out a basic data structure such as sets from the standard library?

4
  • 8
    The language is actually called Go, not golang Nov 28, 2012 at 2:44
  • 103
    But "golang" is more searchable
    – Matt
    May 29, 2013 at 19:27
  • 24
    Way more searchable. Googling "go set" returns images of a wooden board with black and white pieces. Feb 10, 2014 at 5:55
  • I believe the community advice is to use Golang, not Go, for the above reasons - searchability
    – occulus
    Dec 13, 2019 at 11:58

3 Answers 3

74

One potential reason for this omission is that it's really easy to model sets with a map.

To be honest I think it's a bit of an oversight too, however looking at Perl, the story's exactly the same. In Perl you get lists and hashtables, in Go you get arrays, slices, and maps. In Perl you'd generally use a hashtable for any and all problems relating to a set, the same is applicable to Go.

Example

to imitate a set ints in Go, we define a map:

set := make(map[int]bool)

To add something is as easy as:

i := valueToAdd()
set[i] = true

Deleting something is just

delete(set, i)

And the potential awkwardness of this construct is easily abstracted away:

type IntSet struct {
    set map[int]bool
}

func (set *IntSet) Add(i int) bool {
    _, found := set.set[i]
    set.set[i] = true
    return !found   //False if it existed already
}

And delete and get can be defined similarly, I have the complete implementation here . The major disatvantage here is the fact that go doesn't have generics. However it is possible to do this with interface{} in which case you'd have cast the results of get.

10
  • 3
    Here's my slightly-revised version with Contains and Size methods: play.golang.org/p/tDdutH672-
    – Rick-777
    Apr 16, 2013 at 18:07
  • 22
    Instead of map[int]bool one can use map[int]struct{} instead. I prefer the last.
    – oblitum
    Dec 11, 2013 at 2:26
  • 22
    map[int]struct{} .. The struct{} takes 0 bytes. Dec 13, 2013 at 4:34
  • 4
    github.com/fatih/set is an implementation of my based on maps and empty structs. It's thread safe and has a simple api. Jan 11, 2014 at 23:50
  • 6
    With map[int]struct{} you can't do if mymap["key"] { to check for membership. Google recommends using bool (search for "A set can be implemented").
    – Timmmm
    Jan 6, 2015 at 13:18
3

I think this has to do with golang focus on simplicity. sets become really useful with difference, intersection, union, issubset, and so on.. methods. Perhaps golang team felt that it is too much for one data structure. But otherwise a "dumb set" that only has add, contains and remove can be easily replicated with map as explained by @jozefg.

1
  • i disagree. a set is mostly used for membership checks and uniqeness semantics. a set implementation would be perfectly usable without those methods. that being said, they are also trivial to implement. Aug 17, 2018 at 18:23
2

The previous answer works ONLY IF the key are a built-in type. To complement the previous answer, here is a way to implement a set whose elements are user-defined types:

package math

// types

type IntPoint struct {
    X, Y int
}

// set implementation for small number of items
type IntPointSet struct {
    slice []IntPoint 
}

// functions

func (p1 IntPoint) Equals(p2 IntPoint) bool {
    return (p1.X == p2.X) && (p1.Y == p2.Y)
}

func (set *IntPointSet) Add(p IntPoint) {
    if ! set.Contains(p) {
        set.slice = append(set.slice, p)
    }
}

func (set IntPointSet) Contains(p IntPoint) bool {
  for _, v := range set.slice {
    if v.Equals(p) {
      return true
    }
  }
  return false
}

func (set IntPointSet) NumElements() int {
    return len(set.slice)
}

func NewIntPointSet() IntPointSet {
  return IntPointSet{(make([]IntPoint, 0, 10))}
}
3
  • 8
    "works ONLY IF the key are a built-in type" is wrong. type mySet map[IntPoint]bool works perfectly well. All that is required of the key type used in a map is that it has == and !=. Equality of struct types is well defined, your Equals method should be just p1 == p2.
    – Dave C
    Mar 27, 2015 at 1:48
  • 1
    It is true that equality for structs is well-defined, but if the struct contains maps or slices as fields, they will be compared by reference, rather than by value. This may not be what you want. Oct 24, 2018 at 19:42
  • 1
    I take a bit of issue with this solution, because Contains takes linear time, while aMap[] takes constant time, regardless the number of members. A better solution would internally create a unique key based the contents of each member, and leverage the constant-time querying that the map type provides. Even faster solutions that consider cache behavior, etc. exist as well. Oct 24, 2018 at 19:46

Not the answer you're looking for? Browse other questions tagged or ask your own question.