'Generic hashmap in Go
I'm trying to make a wrapper in go for the map type so that I can add some methods like contains() (this almost makes me miss Java). However, I don't know if I can do anything like generics in Java.
While almost everything I've read says that Go doesn't have generic types, there must be a better way than writing a separate struct for every single possible combination of structs and values that I'm using.
Here's what I'm trying to do, even though the code doesn't work:
func newMap(key interface{}, val interface{}) {
keytype := key.(type)
valtype := val.(type)
return hashmap{map[keytype]valtype}
}
type hashmap struct {
hashmap map[]
}
Any help/explanation on how to do this would be appreciated.
EDIT: Contains isn't the only thing I'm trying to replicate, hence the desire to make a "generic" hashmap. replace(), isEmpty(), all that fun stuff as well.
Solution 1:[1]
Golang does not have generics, as you say.
But the maps can be declared using your own types, so you don't necessarily need to write your own "generic" hashmap to use things like "contains" or "is empty".
Example (assuming some Icon and Colour types already exist, as well as the GetTheIcon function that returns some icon):
// this one will map Icon -> Colour
iconToColours := make(map[Icon]Colour)
myIcon := GetTheIcon()
// second return argument is true if the key was found in the map
_, iconIsContained := iconToColours[myIcon]
// check if the map is empty
isEmpty := (len(iconToColours) == 0)
See more here:
Solution 2:[2]
Notwithstanding that a Go map already supports arbitrary types and some basic constructs as outlined in the other answer, starting with Go 1.18 you can actually write a generic map type, with an arbitrary method set:
type Map[K comparable, V any] map[K]V
func (m Map[K,V]) Contains(key K) bool {
_, ok := m[key]
return ok
}
func (m Map[K,V]) GetDefault(key K, dft V) V {
if v, ok := m[key]; ok {
return v
}
return dft
}
func (m Map[K,V]) PutAll(m2 map[K]V) {
for k, v := range m2 {
m[k] = v
}
}
The nice thing about using a generic map type is that it will naturally support the usual operations as indexing and builtin functions (make, delete, ...), so you don't have to redefine those, and instead focus on methods with actual logic:
func main() {
// make and index as usual
m := make(Map[string, int], 0)
m["foo"] = 1
m["bar"] = 2
fmt.Println(m.Contains("foo")) // true
fmt.Println(m.Contains("baz")) // false
fmt.Println(m.GetDefault("baz", 200)) // 200
m2 := Map[string, int]{"a": 10, "b": 20}
m.PutAll(m2)
fmt.Println(m) // map[a:10 b:20 bar:2 foo:1]
}
Playground: https://go.dev/play/p/PmBOdnAMD1_R
Solution 3:[3]
To create functions like contains for maps, you do not need generics. Go can handle this very well, there are more ways to do this, depending on use case some ways can have better performance. You can, for example, consider writing a hashcode function
But there is something more to it to consider.
In Java generics is just code sugar. In runtime, maps or lists in Java have the same functionality as in Golang. The object which becomes visible in a map-action will have features that belong to that object. This is the same in Java and Golang. So in golang you don't have the syntactic sugar, so you have to remember yourself which objects you can excpect in maps, lists, etc.
Read here about syntactic sugar in Java. It will help you accept the choices the Go-designers made.
https://www.cordinc.com/blog/2010/09/java-generics-are-not-real-gen.html
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | |
| Solution 2 | blackgreen |
| Solution 3 |
