- A slice is a data structure describing a contiguous section of an array stored separately from the slice variable itself.
- A slice is not an array.
- A slice describes a piece of an array.
Table of Contents
Golang Slice Example
Given our buffer
array variable from the previous section, we could create a slice that describes elements 100 through 150 (to be precise, 100 through 149, inclusive) by slicing the array:
var slice []byte = buffer[100:150]
//OR
var slice = buffer[100:150]
//OR
slice := buffer[100:150]
// Strucy look something like this
type sliceHeader struct {
Length int
Capacity int
ZerothElement *byte
}
slice := sliceHeader{
Length: 50,
Capacity: 200,
ZerothElement: &buffer[100],
}
Slice Assignments
Empty slice
- capSlice := iBuffer[0:0]
func main() {
var iBuffer [10]int
fmt.Printf("Before: cap(iBuffer)=%d \nlen(iBuffer)=%d\n", cap(iBuffer), len(iBuffer))
fmt.Printf("iBuffer=%#v\n", iBuffer)
// This is important
// This is empty slice
capSlice := iBuffer[0:0]
fmt.Printf("\nAFTER: cap(capSlice)=%d \nlen(capSlice)=%d\n", cap(capSlice), len(capSlice))
fmt.Printf("capSlice=%#v\n", capSlice)
}
******************************
Output
******************************
Before: cap(iBuffer)=10
len(iBuffer)=10
iBuffer=[10]int{0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
AFTER: cap(capSlice)=10
len(capSlice)=0
capSlice=[]int{}
Copy of Slice
- capSlice := iBuffer[:]
func main() {
var iBuffer [10]int
fmt.Printf("Before: cap(iBuffer)=%d \nlen(iBuffer)=%d\n", cap(iBuffer), len(iBuffer))
fmt.Printf("iBuffer=%#v\n", iBuffer)
// This is important
// This is copy of original slice
capSlice := iBuffer[:]
fmt.Printf("\nAFTER: cap(capSlice)=%d \nlen(capSlice)=%d\n", cap(capSlice), len(capSlice))
fmt.Printf("capSlice=%#v\n", capSlice)
}
******************************
Output
******************************
Before: cap(iBuffer)=10
len(iBuffer)=10
iBuffer=[10]int{0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
AFTER: cap(capSlice)=10
len(capSlice)=10
capSlice=[]int{0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
Passing slices to functions
Changing underlying array values
- Below code will change the values of original array even though slice input is copy of the slice header.
- It contains address of underline array.
package main
import "fmt"
func AddOneToEachElement(slice []byte) {
for i := range slice {
slice[i]++
}
}
func main() {
var buffer [50]byte
slice := buffer[10:20]
for i := 0; i < len(slice); i++ {
slice[i] = byte(i)
}
fmt.Println("before", slice)
AddOneToEachElement(slice)
fmt.Println("after", slice)
}
******************************
Output
******************************
before [0 1 2 3 4 5 6 7 8 9]
after [1 2 3 4 5 6 7 8 9 10]
Program exited.
Modifying Slice itself
package main
import "fmt"
func SubtractOneFromLength(slice []byte) []byte {
slice = slice[0 : len(slice)-1]
return slice
}
func main() {
var buffer [50]byte
slice := buffer[10:20]
fmt.Println("Before: len(slice) =", len(slice))
// We have to save updated slice
newSlice := SubtractOneFromLength(slice)
fmt.Println("After: len(slice) =", len(slice))
fmt.Println("After: len(newSlice) =", len(newSlice))
}
******************************
Output
******************************
Before: len(slice) = 10
After: len(slice) = 10
After: len(newSlice) = 9
Program exited.
Pointers to slices: Method receivers
Version-1 : Using Pointer
package main
import "fmt"
func PtrSubtractOneFromLength(slice *[]byte) {
*slice = (*slice)[0 : len(*slice)-1]
}
func main() {
var buffer [50]byte
slice := buffer[10:20]
fmt.Println("Before: len(slice) =", len(slice))
// passed by reference
PtrSubtractOneFromLength(&slice)
fmt.Println("After: len(slice) =", len(slice))
}
******************************
Output
******************************
Before: len(slice) = 10
After: len(slice) = 9
Version-2 : Using Custom Type
package main
import "fmt"
type customType []byte
func (fp *customType) TruncationLastByte() {
*fp = (*fp)[0 : len(*fp)-1]
}
func main() {
buffer := customType("robertPike")
fmt.Println("Before: len(slice) =", len(buffer))
// passed using custom type
buffer.TruncationLastByte()
fmt.Println("After: len(slice) =", len(buffer))
}
******************************
Output
******************************
Before: len(slice) = 10
After: len(slice) = 9
Capacity
Full Slice Check
if cap(slice) == len(slice) {
fmt.Println("slice is full!")
}
Verification of capacity working
package main
import "fmt"
func main() {
var iBuffer [10]int
fmt.Printf("Before: cap(iBuffer)=%d \nlen(iBuffer)=%d\n", cap(iBuffer), len(iBuffer))
fmt.Printf("iBuffer=%#v\n", iBuffer)
// This is important
// This is empty slice
capSlice := iBuffer[0:0]
fmt.Printf("\nAFTER: cap(capSlice)=%d \nlen(capSlice)=%d\n", cap(capSlice), len(capSlice))
fmt.Printf("capSlice=%#v\n", capSlice)
for i := 0; i < 20; i++ {
capSlice = Extend(capSlice, i)
fmt.Println(capSlice)
}
}
func Extend(inputSlice []int, newElement int) []int {
n := len(inputSlice)
// *****************************
// At below line panic will occur
inputSlice = inputSlice[:n+1]
inputSlice[len(inputSlice)-1] = newElement
return inputSlice
}
******************************
Output
******************************
Before: cap(iBuffer)=10
len(iBuffer)=10
iBuffer=[10]int{0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
AFTER: cap(capSlice)=10
len(capSlice)=0
capSlice=[]int{}
[0]
[0 1]
[0 1 2]
[0 1 2 3]
[0 1 2 3 4]
[0 1 2 3 4 5]
[0 1 2 3 4 5 6]
[0 1 2 3 4 5 6 7]
[0 1 2 3 4 5 6 7 8]
[0 1 2 3 4 5 6 7 8 9]
panic: runtime error: slice bounds out of range [:11] with capacity 10
goroutine 1 [running]:
main.Extend(...)
/Users/john.doe/go/src/goexample/slice/capacity.go:29
main.main()
/Users/john.doe/go/src/goexample/slice/capacity.go:19 +0x278
exit status 2
Grow Slice beyond capacity
Use Make
package main
import "fmt"
func main() {
var arr []int = make([]int, 5, 10)
printArr(arr)
// version-1 new slice with double capacity using make
newSlice := make([]int, 2*cap(arr))
// go compiler suggestion as below
// should use copy(to, from) instead of a loop (S1001)go-staticcheck
for i := range arr {
newSlice[i] = arr[i]
}
printArr(newSlice)
}
func printArr(arr []int) {
fmt.Printf("\nlen(arr)=%d cap(arr)=%d\n", len(arr), cap(arr))
fmt.Println("arr=", arr)
}
******************************
Output
******************************
len(arr)=5 cap(arr)=10
arr= [0 0 0 0 0]
len(arr)=20 cap(arr)=20
arr= [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
Using Copy
- The
copy
function is smart. - It only copies what it can, paying attention to the lengths of both arguments.
- In other words, the number of elements it copies is the minimum of the lengths of the two slices.
package main
import "fmt"
func main() {
var arr []int = make([]int, 5, 10)
printSlice(arr)
newSlice := make([]int, 2*cap(arr))
// version-2 new slice with double capacity using make
// using copy
copy(newSlice, arr)
printSlice(newSlice)
}
func printSlice(arr []int) {
fmt.Printf("\nlen(arr)=%d cap(arr)=%d\n", len(arr), cap(arr))
fmt.Println("arr=", arr)
}
******************************
Output
******************************
len(arr)=5 cap(arr)=10
arr= [0 0 0 0 0]
len(arr)=20 cap(arr)=20
arr= [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
Use case
Inserts the value into the slice at the specified index
Version-1: Creating new slice
package main
import "fmt"
func main() {
input := []int{11, 12, 13, 14, 15, 16, 17, 18, 19, 20}
// case-1: Creating new slice
arr := input
fmt.Println("\nInitialization")
printSlice(arr)
// // case-2: Extending existing slice by 1
// arr := make([]int, 15, 20)
// fmt.Println("Original")
// printSlice(arr)
// copy(arr[0:], input)
// fmt.Println("\nInitialization")
// printSlice(arr)
fmt.Println("\nBefore")
printSlice(arr)
arr = insertAtIndex(arr, 5, 28)
fmt.Println("\nAfter")
printSlice(arr)
}
func insertAtIndex(arr []int, index int, newElement int) []int {
if index < 0 || index > len(arr) {
fmt.Println("Index is out of bound")
return arr
}
newSlice := arr[:]
if len(arr) == cap(arr) {
// Slice is full; must grow.
// We increase its size by 1, so if the size is zero we still grow.
fmt.Println("\nCreating new slice")
newSlice = make([]int, len(arr)+1, 2*cap(arr))
copy(newSlice, arr)
} else {
fmt.Println("\nExtending existing slice by 1")
newSlice = arr[:len(arr)+1]
}
copy(newSlice[index+1:], newSlice[index:])
newSlice[index] = newElement
return newSlice
}
func printSlice(arr []int) {
fmt.Printf("len(arr)=%d cap(arr)=%d\n", len(arr), cap(arr))
fmt.Println("arr=", arr)
}
******************************
Output
******************************
Initialization
len(arr)=10 cap(arr)=10
arr= [11 12 13 14 15 16 17 18 19 20]
Before
len(arr)=10 cap(arr)=10
arr= [11 12 13 14 15 16 17 18 19 20]
Creating new slice
After
len(arr)=11 cap(arr)=20
arr= [11 12 13 14 15 28 16 17 18 19 20]
Version-2: Extending existing slice by 1
package main
import "fmt"
func main() {
input := []int{11, 12, 13, 14, 15, 16, 17, 18, 19, 20}
// // case-1: Creating new slice
// arr := input
// fmt.Println("\nInitialization")
// printSlice(arr)
// case-2: Extending existing slice by 1
arr := make([]int, 15, 20)
fmt.Println("Original")
printSlice(arr)
copy(arr[0:], input)
fmt.Println("\nInitialization")
printSlice(arr)
fmt.Println("\nBefore")
printSlice(arr)
arr = insertAtIndex(arr, 5, 28)
fmt.Println("\nAfter")
printSlice(arr)
}
func insertAtIndex(arr []int, index int, newElement int) []int {
if index < 0 || index > len(arr) {
fmt.Println("Index is out of bound")
return arr
}
newSlice := arr[:]
if len(arr) == cap(arr) {
// Slice is full; must grow.
// We increase its size by 1, so if the size is zero we still grow.
fmt.Println("\nCreating new slice")
newSlice = make([]int, len(arr)+1, 2*cap(arr))
copy(newSlice, arr)
} else {
fmt.Println("\nExtending existing slice by 1")
newSlice = arr[:len(arr)+1]
}
copy(newSlice[index+1:], newSlice[index:])
newSlice[index] = newElement
return newSlice
}
func printSlice(arr []int) {
fmt.Printf("len(arr)=%d cap(arr)=%d\n", len(arr), cap(arr))
fmt.Println("arr=", arr)
}
******************************
Output
******************************
Original
len(arr)=15 cap(arr)=20
arr= [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
Initialization
len(arr)=15 cap(arr)=20
arr= [11 12 13 14 15 16 17 18 19 20 0 0 0 0 0]
Before
len(arr)=15 cap(arr)=20
arr= [11 12 13 14 15 16 17 18 19 20 0 0 0 0 0]
Extending existing slice by 1
After
len(arr)=16 cap(arr)=20
arr= [11 12 13 14 15 28 16 17 18 19 20 0 0 0 0 0]
Append
package main
import "fmt"
func main() {
slice1 := []int{11, 12, 13}
slice2 := []int{14, 15, 16}
printSlice(slice1)
printSlice(slice2)
// slice1 = appendV1(slice1, slice2...)
slice1 = appendV2(slice1, slice2...)
printSlice(slice1)
slice1 = appendV2(slice1, 100)
printSlice(slice1)
slice1 = appendV2(slice1, 101, 102)
printSlice(slice1)
}
func appendV2(slice []int, elements ...int) []int {
// efficient version
n := len(slice)
total := len(slice) + len(elements)
if total > cap(slice) {
newSlice := make([]int, total, 2*total+1)
copy(newSlice, slice)
slice = newSlice
}
slice = slice[:total]
copy(slice[n:], elements)
return slice
}
func appendV1(input []int, items ...int) []int {
// Simpler version
total := len(input) + len(items)
var resultSlice []int
if total > cap(input) {
fmt.Println("Creating new slice")
resultSlice = make([]int, total, 2*total+1)
copy(resultSlice, input)
} else {
resultSlice = input[:total]
}
copy(resultSlice[len(input):], items)
return resultSlice
}
func printSlice(arr []int) {
fmt.Printf("len(arr)=%d cap(arr)=%d\n", len(arr), cap(arr))
fmt.Println("arr=", arr)
}
******************************
Output
******************************
len(arr)=3 cap(arr)=3
arr= [11 12 13]
len(arr)=3 cap(arr)=3
arr= [14 15 16]
len(arr)=6 cap(arr)=13
arr= [11 12 13 14 15 16]
len(arr)=7 cap(arr)=13
arr= [11 12 13 14 15 16 100]
len(arr)=9 cap(arr)=13
arr= [11 12 13 14 15 16 100 101 102]
Visit https: https://codeandalgo.com for more such contents
Leave a Reply