Struct, Map, Array and Slice

Struct

GO is not a traditional OO language and hence does not have concept of class. Instead structures are used for holding values. If you are familiar with Java or .Net (may be true for some other OO languages too), you can co-relate struct to beans.

type Person struct {
	FirstName string
	LastName  string
	Age       int
}

Here, we have defined Person of type struct, to hold values for properties/fields related to person like first name, last name and age.

Let us see, at various ways to initialize and use a struct:

func main() {
	var p1 Person
	p2 := Person{}
	p3 := Person{"ABC", "XYZ", 32}
	p4 := Person{LastName: "XYZ", FirstName: "ABC"}
	p5 := new(Person)

	fmt.Println("Person 1 :", p1)
	fmt.Println("Person 2 :", p2)
	fmt.Println("Person 3 :", p3)
	fmt.Println("Person 4 :", p4)
	fmt.Println("Person 5 :", p5)
}

http://play.golang.org/p/W6yptQsvpB

Output:
Person 1 : { 0}
Person 2 : { 0}
Person 3 : {ABC XYZ 32}
Person 4 : {ABC XYZ 0}
Person 5 : &{ 0}

As seen from output,

  1. var p1 Person: This will initialize struct Person with zero value, i.e. will initialize all the fields in struct to their default values (string to empty string, int to 0 and so on).
  1. p2 := Person{}: Initialize struct Person, with default value i.e. each field/property of struct is initialized to default value. Since FirstName and LastName are defined as string they are initialized to “” (empty string) and Age is defined as int, initialized to 0.
  1. p3 := Person{“ABC”, “XYZ”, 32}: This will initialize Person with values passed i.e. will set FirstName as ABC, LastName as XYZ and Age as 32. Important to note here is that fields of struct will be initialized in sequence of the values passed.

    Try following code where we have interchanged the 1st two values passed and see the output, value of FirstName and LastName also gets interchanged.

    p3 := Person{“XYZ”, “ABC”,  32}

    In case if there is mismatch in the values passed and order of type of fields defined, error will be throws.

    Try p3 := Person{32, “XYZ”, “ABC”} and you will see following errors:
    cannot use 32 (type int) as type string in field value cannot use “XYZ” (type string) as type int in field value

    Hence if you wish not to pass value for all the fields and want to skip any field in between from setting value you the following way of initializing the struct.

  1. p4 := Person{LastName: “XYZ”, FirstName: “ABC”}: This will initialize p4, with Person and will set FirstName and LastName. In this case we are explicitly specifying what should set as LastName and FirstName. So order is not important.
  1. p5 := new(Person): Will initialize structPerson, with default value set for each of the fields in struct. Built-in function new will allocate memory and return back pointer.

Map

Map is the only collection with key/value pair supported by GO. Map with any type as key and value can be created. Let us look at how we can create a map:

m := make(map[int]string)

Here we have created map, with int as key and string as value. Built-in function make is used for creating map as shown above.  We can also pass initial capacity as shown below:

m := make(map[int]string, 10)

This will create map with initial capacity 10.

Let us see an example with various operations on map:

func main() {
	m := make(map[int]string)
	fmt.Println("Values in map (after creating): ", m)
	m[0] = "ABC"
	m[1] = "PQR"
	m[2] = "XYZ"

	fmt.Println("Length of map: ", len(m))
	fmt.Println("Values in map(after adding values): ", m)

	m[1] = "LMN"
	fmt.Println("Values in map (after updating): ", m)

	delete(m, 1)
	fmt.Println("Values in map: ", m)

	for k, v := range m {
		fmt.Println("Key :", k, " Value :", v)
	}

	fmt.Println("Value for not existing key : ", m[3])
}

http://play.golang.org/p/iBNKcZ9XNO

Output:

Values in map (after creating): map[]
Length of map: 3
Values in map(after adding values): map[0:ABC 1:PQR 2:XYZ]
Values in map (after updating): map[0:ABC 1:LMN 2:XYZ]
Values in map: map[0:ABC 2:XYZ]
Key : 0 Value : ABC
Key : 2 Value : XYZ
Value for not existing key :

First print statement prints map m just after creating, hence we can see empty map.  Then we add 3 map entries with values ABC, PQR and XYZ for key 0, 1 and 2 respectively. As you can see adding value to map is simple assignment of format map[key] = value. Something similar to the way value is assigned in array. Updating value is also done is same way as assigning, so in case if key is not present key, it is  added and value is assigned and if key exists its value is updated.

Note the output, for printing value for not existing key, it is empty string. Since map is holding string values, for not existing key zero value string is returned which is empty string. If map was holding int values, 0 would be returned.

Use built-in function len for getting length of map as shown above. There is built-in function delete for removing entry from map. As shown above we have used delete to remove entry for key 1.

Array

A data type used for holding elements of the same type. Let us look at an example to see how arrays can be declared in GO.

a := [5]int{}

Here we have defined array of type int, which can hold 5 values. Array of any type can be defined. Arrays are 0 indexed, means first element in array is referred with index 0 so if length of array is 5 that means indexes are from 0 to 4.

Arrays are of definite length, so an array can hold only that number of values.

Example:

func main() {
	a := [5]int{}
	fmt.Println("Values in array a (after creation) : ", a)
	fmt.Println("Length of array a : ", len(a))

	for i, _ := range a {
		a[i] = i
	}

	fmt.Println("Values in array a (after updating) : ", a)

	b := [...]string{"A", "B", "C", "D", "E"}
	fmt.Println("Values in array b : ", b)
}

http://play.golang.org/p/gf2qtFGbB9

Output:
Values in array a (after creation) : [0 0 0 0 0]
Length of array a : 5
Values in array a (after updating) : [0 1 2 3 4]
Values in array b : [A B C D E]

We have declared 2 arrays, a and b. Array a is of type int and of length 5. Since array a is of type int, if we print its value just after creation, they are all set to 0. Array b, is declared to hold values of type string. Array b we haven’t set length, but instead array gets its length from the number of values that it is holding. Since we are not assigning length we use three dots ([…]) to indicate the same.

Use built-in function len to check length of array.

Example above shows how to iterate over array. While iterating, range returns 2 values index(key in case of map) and value. Since in for loop we are not using value we have skipped it and for doing so we using _ (underscore) as 2nd value in for loop.

In GO, whenever we want to skip any value returned, we can use _ (underscore). It is also known as Blank Identifier. If we do not want to use any value and we assign it to any variable, GO compiler will complain for unused variable and hence we are using the Blank Identifier since we are not interested in the value.

All the arrays defined and used above, are single dimension array, GO also supports creating a multi-dimensional array. For example, see 2 dimensional array below:

a := [5][4]int{}

Here we will have 5 elements each will be have 4 values. Since array is of type int, they all will be initialized to 0.

[[0 0 0 0] [0 0 0 0] [0 0 0 0] [0 0 0 0] [0 0 0 0]]

Slice

Slice is a special data type that wraps on top of array. Slice can represent either entire array or portion of array as needed. Unlike array which has its own storage, slice share the same storage as of the underlying array and hence multiple slice on same array will share same memory. Slice is created using built-in function make, as shown below:

s := make([]int, length, capacity)

length: Used to define length of the slice. Length can be any non-negative number less than capacity (if provided). Use built-in function len to get length of slice.

capacity: Used to define capacity of underlying array. It is optional to provide capacity, and if not mentioned capacity is equal to length.

Example:

s := make([]int, 5, 10)

Here slice s, of length 5 is created with capacity 10. So this mean slice s can hold max 5 values, but underlying array can hold 10 values.

Slice can also be created in same as array, by skipping the preceding length as shown below:

s := []int{0, 1, 2, 3}

It is slice and not array which is mostly used in GO, and hence it is very important to understand how it is used. It is not always that we create empty slice, but we also create slice on top of already defined array as shown below:

func main() {

	a := [...]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}

	s := a[0:5]

	fmt.Println(s)
}

http://play.golang.org/p/vRishSMCcU

Output:
[1 2 3 4 5]

Here we created array a of type int having values from 1 to 10. Then we create a slice on top of the array a, a of slice of length 5 (a[0:5]) i.e. with index 0 to 4. If you look at the output we have printed values in slice s,  it has 1st five values from array a. This is one of the use-case of slice, creating subset of array.

Let us look at another example:

func main() {

	a := [...]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}
	s1 := a[0:5]
	s2 := a[2:7]

	fmt.Println("Array :", a)
	fmt.Println("Slice 1 :", s1)
	fmt.Println("Slice 2 :", s2)

	s1[3] = 11

	fmt.Println("------- After Updating ----------")
	fmt.Println("Array :", a)
	fmt.Println("Slice 1 :", s1)
	fmt.Println("Slice 2 :", s2)
}

http://play.golang.org/p/EmsxZXuf1N

Output:

Array : [1 2 3 4 5 6 7 8 9 0]
Slice 1 : [1 2 3 4 5]
Slice 2 : [3 4 5 6 7]
——- After Updating ———-
Array : [1 2 3 11 5 6 7 8 9 0]
Slice 1 : [1 2 3 11 5]
Slice 2 : [3 11 5 6 7]

In this example, we created 2 slice s1 and s2 each of length 5 and on top of array a. But slice s1 and s2, both represent different range of values of the underlying array a.

Look at the output for values in slice s1 and s2 and you will see slice s1 have 1st five values and slice s2 have 5 values from array starting from index 2, this is because we created slice s2 as a[2:7].

We updated value at index 3 on slice s1 to 11, and then printed values in both the slices and you can see that value in slice s2 is also updated. This is because both the slice s1 and s2 are created on top of array a. Also check the value of array at index 3, it has also got updated.This is because slice s1 and s2 on array a, and they all share same memory.

If we create slice on top of array, as s1 := a[:], this means that slice with length equivalent to the length of underlying arrayawill be created.

Now let us look at the example below to understand why slice is used more than array and why it is not just a subset of array.


func main() {

	a := [...]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
	s1 := a[:]

	fmt.Println("Array :", a)
	fmt.Println("Slice 1 :", s1)
	fmt.Println("Length of Slice 1 :", len(s1))
	fmt.Println("Capacity of Slice 1 :", cap(s1))

	s1 = append(s1, 11)

	fmt.Println("---------- After adding new value ------------")
	fmt.Println("Array :", a)
	fmt.Println("Slice 1 :", s1)
	fmt.Println("Length of Slice 1 :", len(s1))
	fmt.Println("Capacity of Slice 1 :", cap(s1))
}

http://play.golang.org/p/JkJz_DbkPe

Output:

Array : [1 2 3 4 5 6 7 8 9 10]
Slice 1 : [1 2 3 4 5 6 7 8 9 10]
Length of Slice 1 : 10
Capacity of Slice 1 : 10
———- After adding new value ————
Array : [1 2 3 4 5 6 7 8 9 10]
Slice 1 : [1 2 3 4 5 6 7 8 9 10 11]
Length of Slice 1 : 11
Capacity of Slice 1 : 20

Here, we created slice s1, on array a. Since we haven’t specified range while creating slice s1, it will represent complete array a, and hence length of slice s1 is 10. See the output to check the values, length and capacity of s1.

We used built-in function append, to add value to slice s1. And now print values, length and capacity and you will see newly added value and new length of slice s1 is 11 now. But if you look at new capacity it is 20, double the previous capacity.

Note, the value of array, before and after adding new value to slice, it is same. This is because when we created array, we had passed 10 values and hence length of array is 10 and addition of new value to slice will not increase the length of array.

Conclusion

Today, we got introduced to struct, map, array and slice. GO does not have too many data types or collections to be used and hence what we have learned today is what gets used in GO applications. In next post we will learn about functions and pointers.

Leave a comment