Go Data Structures

As we have seen how control flow and functions work in Chapter – 4 of our Golang tutorial, let’s have a look at major Golang Data Structures.

 

Array –

Well, almost every programmer knows what is an array. But let’s revisit the concept one more time –

Basically, an array is a number of elements of the same type stored in sequential order. We use arrays to store fixed no. of more elements which is really a good data structure.

We need to follow the below syntax to create an array :

var var_name [size]var_type

Example –

var arr [10]int //It will create an array with name”arr” of 10 integers
  • Once you declare an array with its size you are not allowed to change it.
  • If you try to insert more elements than array size, the compiler will give you an error.
  • By default array size is 0 (zero)
  • Array index starts from the 0th index
  • We can set value directly to array at particular index array_name[index]=value
  • The inbuilt lens returns the length of an array

Learn about Golang fundamentals here as a starting point.

Go provides a convenient way of storing elements.
func main(){
 var x [5]int
 x[3] = 1000
 fmt.Println(x)
b:=[5]int {54,76,34,2,3,} /*we have initialized this array and notice extra trailing , 
after last element of array which is required in Go. we no need to specify type because Go can figure it out */

}
main.go
package main
import (
 "fmt"
)
func main() {
 var name [50]string //array of 50 string elements
 fmt.Println(name)
 name[43] = "Welcome to Go Programming"
 fmt.Println(name)
 var x [100]string
 fmt.Println("Length of arr x:", len(x)) //calculate length of array
 z := [5]int{43, 78, 54, 23, 22}
 fmt.Println(z) 
 for i := 65; i <= 122; i++ {
 x[i-65] = string(i)

 }
 fmt.Println(x)
}

Data Structures: Slice

Slice is the same as an array but it has a variable length so we don’t need to specify the length to it. It will grow whenever it exceeds its size. Like an array, slice also has index and length but its length can be changed.

  • Slice also has continuous segments of memory locations
  • The default value of uninitialized slice is nil
  • Slices does not store the data. It just provides reference to an array
  • As we change the elements of slice, it will modify corresponding elements of that array

Syntax for slice creation is:

var var_name [ ]var_type

The only difference between slice and array is that there is no need to specify length while creating a slice.

var x[ ] float64

Here, a slice of type float64 elements will be created with length 0 & capacity 0

func main() {
nums:=[ ]int {5,7,4,8,9} //No need to specify length
fmt.Println(nums)
}

We can also create slice by using make() also which is available in builtin package of golang

func make(Type,size IntegerType)Type
 x:=make([ ]string,5) → Here the capacity of slice equal to length of it

This will create a slice of underlying string array having a size for 5 elements

Length→ No. of elements present

Capacity→ Total Size

The length and capacity of a slice can be obtained by using len(slice), cap(slice)

Below is another example –

array:=[ ]float64 {1,2,3,4,5} //slice of float64 elements
x:=array[0:5] 
fmt.Println(x)

Normally we switch the ON value of a variable but Golang control allows you to switch ON type also.

var a [10]int → equivalent as a[0:10], a[:10], a[0:], a[:]
You can do slicing on a string because the string is a slice of bytes.

Remember → String is made up of runes (Uni Code). A rune is Unicode UTF Code point (for ‘A’-65’, ‘a-97’). A Unicode code point is 1 to 4 bytes.

So, strings are made up of runes, runes are made up of bytes, so strings are made up of bytes. A string is a bunch of bytes, a slice of bytes.

You can access slice elements by using a range-in for a loop.

There are built-in functions to calculate length by using len(slice), capacity by using cap(slice), append one slice to another by using append(slice) and copy(slice) to copy one slice to another.

We can create a slice in three ways:

  • Using var
  • Shorthand Notation
  • make( )
func main() {
 elements := []int{3, 5, 2, 6, 2}
 arrays := make([ ]int, 0, 3) //Creating slice with make( )
 for n := range elements {
 fmt.Println("Elements", n)
 }
 for i := 0; i < 80; i++ {
 arrays = append(arrays, i) // to append i elements to slice
fmt.Println("Len:", len(arrays), "Capacity:", cap(arrays),
 "Value: ", arrays[i])
 }}

Here in the above program, you can see how length and capacity increase when it exceeds size limits. The capacity will be doubled when the size exceeds the capacity.

If we think that our slice might grow, we can set capacity which is larger than length. This gives the slice room to grow without creating a new array every time our slice grows.

We also can create slice inside another slice like –

sliceOne:=make([ ][ ]int,0) //slice of slice of int
main.go
package main
import (
 "fmt"
)
func main() {
 primes := []int{1, 3, 5, 7, 9, 11}
 fmt.Printf("%T\n", primes) //type []int
 fmt.Println(primes) //[1 3 5 7 9 11]
 fmt.Println(primes[2:4]) // [5 7] 3rd 4th element

 var info []byte //Slice using var
 elements := []int{3, 5, 2, 6, 2} //Slice using shorthand notation
 arrays := make([]int, 0, 3) //Creating slice with make( )
 //String slice
 str := []string{"Amazing", "Brilliant", "Good One", "Excellent"}
 for n := range elements {
 fmt.Println("Elements", n)
 }
 for i := 0; i < 80; i++ {
arrays = append(arrays, i) //append i elements 
to arrays
 fmt.Println("Len:", len(arrays), "Capacity:", 
cap(arrays), "Value: ", arrays[i])
 }

 fmt.Println("Info elements:", info)
 for i, vals := range str {
 fmt.Println("Values", i, vals)
 }
 //Slice of Slice
 OutIn := make([][]int, 0, 3)
 for i := 0; i < 3; i++ {
 out := make([]int, 0, 4)
 for j := 0; j < 4; j++ {
 out = append(out, j)
 }
 OutIn = append(OutIn, out)
 }
 fmt.Println(OutIn)
}

Are You Looking For Golang Development Services?

Data Structures: Map

One of the most important and useful data structure in computer science is the Hash Table. Golang provides a map data structure that implements hashtable. A unique key-value pair like a dictionary is used to look up values based on the key.

map [keyType] valueType
Map is represented by keyword map having key type in [ ]and value types.
The default value of map is nil as it is reference type like pointer or slice and for nil key and value pairs are 0.
This includes unordered pairs. Maps are like literals but unique keys are required.
var dictionary map[string]int //map using var
 dictionary[“Zero”]=1
 fmt.Println(dictionary[“Zero”]) //Accessing value using key

Here map name is dictionary having key-value pairs of string & int type.

The values are accessed using key values. The maps can be created by using make( ) also which is more effective to use.

dictionary:=make(map[string]int) //map using make()

The make() creates a hash data structure and return map value pointed by key. Go provides familiar syntax to work with a map data structure.

The functions provided are len(map) and delete(mapName,key)

func main(){
 var details = map[int]string{101: "Jordan", 102: "Roger", 103: "Rafel"} //using var 
fmt.Println(details)
 fmt.Println(details[102])
 countries := make(map[string]string) //Shorthand and make
 countries["US"] = "United States"
 countries["IND"] = "India"
fmt.Println(countries)
}

Comma OK idiom: If we are looking for value related to a particular key then map returns two values: one value if found and value that indicates success or failure(not present). We call this comma OK idiom.

func main(){
countries := make(map[string]string) 
 countries["US"] = "United States"
 countries["IND"] = "India"
if val,ok:=countries[“IND”] ; ok{ //comma ok idiom
 fmt.Println(“Value”,val,”is present”)
 fmt.Println(ok) //it will return true as ok is bool type
}

Range: Range is used to iterating over the slice and map along with for loop. When a range is used with slice it returns two values- 1st one is an index and 2nd one is a copy of the element at that index.

func main(){
countries := make(map[string]string) 
 countries["US"] = "United States"
 countries["IND"] = "India"
 for country, capital := range countries {
 fmt.Println(country, "-->", capital)
 }
}
main.go
package main
import (
 "fmt"
)
func main() {
 var details = map[int]string{101: "Jordan", 102: "Roger", 103: "Rafel"}
 fmt.Println(details)
 fmt.Println(details[102]) //Accessing value based on keys
 countries := make(map[string]string) //Shorthand and make
 countries["US"] = "United States"
 countries["IND"] = "India"
 countries["UK"] = "United Kingdom"
 fmt.Println(countries)
 capitals := map[string]string{ //Shorthand composite literal
 "India": "Delhi",
 "US": "Washington"}
 capitals["UK"] = "Great Britian" //Adding an entry
 capitals["NZ"] = "Wellington"
 capitals["UK"] = "London" //Updating entry
 fmt.Println(capitals, " of length", len(capitals)) //len() for Length
 delete(capitals, "NZ") //deleting entry
 fmt.Println(capitals)
 //Comma OK
 if value, present := capitals["US"]; present {
 fmt.Println("Value", value, "is present")
 fmt.Println("Status:", present)
 }
 for country, capital := range countries {
 fmt.Println(country, "-->", capital)
 }
 //map inside map
 CountryInfo := map[string]map[string]int{
 "INDIA": {"IN": 91},
 "United States of America": {"US": 1}, //always give the,after last 
element
 }
 for key, val := range CountryInfo {
 fmt.Println(key, "--->", val)
 }
}

Data Structure – Struct

  • Encapsulation → state [“fields”] behaviour [“methods”] export / unexported
  • Reusability → Inheritance [“Embedded Types”]
  • Polymorphism → Interfaces
  • Overriding → Promotion

Golang doesn’t have keyword ‘Class’ but it has ‘struct’ which is contains named collection of fields/properties. The struct is also there in ‘C’ language.

Struct→

  • user-defined type
  • we declare the type
  • the type has fields
  • the type can also have “tags”
  • the type has an underlying type
  • in this case, underlying type is struct
  • we declare variables of the type
  • we initialize those variables
  • initialize with a specific value or with default values

In some object-oriented languages, methods are composed within struct or class

Class Animal{
 public void eat() { } //Method defined in the class itself
 }

But in Golang, they are ‘associated’ with struct.

type Animal struct{ }
func (e Animal ) eat () { } //Methods defined outside but works on same struct

A struct is defined by using type and struct keyword along with the meaningful name

type House struct { }
type Car struct { }
type #Mobile struct { } //invalid

Here type defines new type followed by name of struct and then struct keyword

Example –

type Mobile struct {
 brand string
 model string
 price int 
 }

We can create an instance of our struct Mobile in many ways like

var mob Mobile

This will create local variable mob and sets the default value to its fields (0 for int, “ ” for string, 0.0 for float)

mob:=new(Mobile)

It will allocate memory to all fields by setting default values and returning a pointer to it (*Mobile). More often we need to give values to each of the fields. We can do this in two ways:

mob:=Mobile{brand:“Samsung”,model:”Galaxy”,price:24500}

OR

mob:=Mobile {“Samsung,Galaxy,24500”}

Fields →

We need to define a meaningful name & type for fields or properties. We can initialize fields with its value or can keep blank so it will use the default value.

We have to access the struct fields with “. (dot)” only.

Accessibility and Visibility of struct and variables

There is no concept like public, private, protected for the visibility purpose in Golang. As Golang is simple, if the first letter of the struct name or variable name is capital then it is visible outside that package.

type Mobile struct{ //this struct can be exported
 brand string //not exported
 Model string //exported
 }

Methods

Go supports methods to define a struct.

func (receiverName receiverType) functionName(arguments) returnType

In between the keyword func and functionName, we’ve added the receiver. “Receiver” is like parameter-it has its name and type, and by creating this we have to call the function using “.” operator.

You cannot declare a method with a receiver whose type is defined in another package.

Sample Code

func (mob *Mobile) show () string { //func associated with Mobile
 mob.brand = "Xiomi"
 return mob.brand
}
 ……….
 var mob Mobile
 fmt.Println(mob.show())

Methods having pointer receivers can modify the value to which the receiver points.

We can define a method on any type defined in struct as well as from same package but you cannot define method on type from another package.

main.go
package main
import (
 "fmt"
)
type width int //user defined type
type Mobile struct {
 brand string
 model string
 price int
}
func (mob Mobile) display() string { //func associated with Mobile
 mob.brand = "Xiomi"
 return mob.brand
}
func (mob *Mobile) show() string { //func associated with Mobile
 mob.brand = "Xiomi"
 return mob.brand
}
func main() {
 var height width
 fmt.Println(height)
 m := Mobile{}
 fmt.Println(m) //Default values inside struct{" " 0}
 var mob Mobile //Instance creation using var
 fmt.Println(mob)
 mobs := new(Mobile)
 fmt.Println(mobs)
 phone := Mobile{"Samsung", "Galaxy", 24500} //Struct initialization
 fmt.Println("Before Change:", phone)
 fmt.Println("Function Call", phone.display()) // Xiomi
 fmt.Println("After Change:", phone) //still old values are coming 
 {"Samsung","Galaxy",24500}
 fmt.Println("Function Call:", phone.show()) //calling show()
 fmt.Println("After Change:", phone) //Here changed values will 
 reflect
}

Type Aliasing –

We can create aliases for the type that you want to use.

main.go
 package main
 import (“fmt” “strings”)
 type s string
 func (myStr s) Upper() string{
 return strings.ToUpper(string(myStr))
 }
 func main(){
 fmt.Println(s(“hello”).Upper())
}

We can also attach methods to the types. See the “time” package having type Duration – https://golang.org/pkg/time/#Duration

Embedded Types –

A struct’s properties/fields usually represent has-a relationship like an inheritance. Suppose we have a struct person.

type Person struct {
 Name string
 }
 func (per *Person) speak () {
 fmt.Println(“Hello, I’m”,per.Name)
}

And now if we want to create a new struct. We can do like this –

type Mobile struct{
 Person //Embedded type
 Model string
 }

So, Go supports relationships like this by using embedded type, also known as anonymous fields.

Here, we are using type Person by embedding into Mobile. Now to access the struct Person fields we can use type name.

mob:=new(Mobile)
 mob.Person.speak( )

Promotion –

Embedding is the composition, not inheritance, but Go supports something called “promotion” where the fields and methods of the embedded type become available to the embedding type.

This is something like inheritance but actually not inheritance.

main.go
package main
import (
 "Fmt"
)
type Person struct {
 FirstName string
 LastName string
 Age int
}
func (per *Person) show() {
 fmt.Println(per.FirstName, "", per.LastName, "", per.Age)
}
func (cust *Customer) show() { //method overriding based on receiver
 fmt.Println(cust.ContactNo)
}
type Customer struct {
 Person //embedded type
 ContactNo int
}

func main() {
 cust := Customer{
 Person: Person{
 "John",
 "Matthew",
 25,
 },
 ContactNo: 9965,
 }
 cust.Person.show()
 cust.show()
}

Go automatically handles conversion between values and pointers for method calls. You may want to use a pointer receiver type to avoid copying method calls.

Struct Pointer

We can use struct pointer to access struct fields –

mob:=&Mobile {“Lenovo”,”A6”,”6700”}
 fmt.Println(mob.brand,mob.model,mob.price)

Conclusion 

Well, we have covered some of the major Data Structures for Golang in this chapter. In Part II of this chapter, we will dive deeper and explore some more Golang Data Structures. We as a Golang development company help businesses develop applications. Read more on our Golang Tutorial to learn about efficient practices related to GoLang.

Content Team

This blog is from Mindbowser‘s content team – a group of individuals coming together to create pieces that you may like. If you have feedback, please drop us a message on contact@mindbowser.com

Keep Reading

  • Service
  • Career
  • Let's create something together!

  • We’re looking for the best. Are you in?