Loading...
Loading...

Go Arrays and Slices Tutorial

Arrays and slices are Go's primary sequential data structures. While arrays are fixed-size value types, slices are dynamic, flexible views into underlying arrays that power most list-like operations in Go.

1. Arrays - Fixed-Size Sequences

Arrays are fixed-length, homogeneous collections where the size is part of the type itself.

// Declaration with zero values
var days [7]string  

// Literal initialization
primes := [5]int{2, 3, 5, 7, 11}

// Ellipsis for inferred length
temps := [...]float64{98.6, 99.2, 97.9}

// Access/modify elements
primes[0] = 1      // First element (index 0)
len := len(primes) // Length is part of type

Key Characteristics:

  • Fixed size - Cannot be resized after creation
  • Value type - Copies entire array when assigned or passed
  • Memory layout - Contiguous block of elements
  • Type includes size - [5]int and [10]int are distinct types

Arrays Quiz

What happens when you assign one array to another?

  • They share the same memory
  • A full copy is made
  • It creates a reference

2. Slices - Dynamic Views

Slices are flexible, dynamically-sized views into underlying arrays.

// Slice literals
colors := []string{"red", "green", "blue"}

// From array
arr := [5]int{1, 2, 3, 4, 5}
slice := arr[1:4] // [2, 3, 4]

// Using make
nums := make([]int, 3, 5) // len=3, cap=5

// Common operations
colors = append(colors, "yellow")  // Add element
copy(nums, []int{7, 8, 9})        // Copy between slices

Slice Internals:

  • Three components: pointer, length, and capacity
  • Reference type - Points to underlying array
  • Resizable - Via append() function
  • Zero value is nil (no underlying array)

Slices Quiz

What makes a slice different from an array?

  • Slices can't be resized
  • Slices are references to arrays
  • Arrays are always more efficient

3. Slice Operations

Slices support powerful operations for manipulation and access.

s := []int{10, 20, 30, 40, 50}

// Slicing (creates new slice view)
firstTwo := s[:2]   // [10, 20]
lastThree := s[2:]  // [30, 40, 50]
middle := s[1:4]    // [20, 30, 40]

// Length vs Capacity
fmt.Println(len(s), cap(s)) // 5, 5

// Appending (may allocate new array)
s = append(s, 60, 70)

// Copying
dest := make([]int, 2)
copied := copy(dest, s) // Returns number copied

Important Behaviors:

  • Slicing creates new views of same array (no copy)
  • Appending beyond capacity allocates new array
  • Modifications affect all slices sharing same array
  • Use copy() for independent duplicates

Operations Quiz

What happens when you append to a full slice?

  • The program panics
  • Go allocates a new larger array
  • Elements are overwritten

4. Memory Efficiency

Understanding slice memory management is crucial for performance.

// Pre-allocate with make
items := make([]string, 0, 100) // len=0, cap=100

// Re-slicing to free memory
bigSlice := make([]byte, 1e6)
// Keep only first 10 elements
bigSlice = bigSlice[:10:10] // Reset capacity

// Clearing without reallocation
names := []string{"Alice", "Bob", "Charlie"}
names = names[:0]  // len=0, but keeps capacity

Optimization Techniques:

  • Pre-allocate capacity when size is known
  • Re-slice to release unused memory
  • Reuse slices by resetting length
  • Beware of leaks - Slices can keep large arrays alive

Memory Quiz

How can you free memory from a large slice?

  • By setting it to nil
  • By re-slicing with reduced capacity
  • Using the free() function

5. Common Slice Patterns

Idiomatic Go uses slices in several standard patterns.

// Stack operations
stack := []int{1, 2, 3}
stack = append(stack, 4) // push
top := stack[len(stack)-1] // peek
stack = stack[:len(stack)-1] // pop

// Removing elements
func remove(s []string, i int) []string {
    return append(s[:i], s[i+1:]...)
}

// Filtering in place
n := 0
for _, x := range data {
    if isValid(x) {
        data[n] = x
        n++
    }
}
data = data[:n]

Useful Patterns:

  • Stack operations using append and re-slicing
  • In-place filtering by compacting valid elements
  • Batch processing by slicing into chunks
  • Zero-copy conversions between compatible types

Patterns Quiz

How would you implement a stack with slices?

  • Using a linked list
  • append for push, re-slice for pop
  • With a separate index variable

6. Strings as Byte Slices

Strings behave similarly to read-only byte slices with special syntax.

s := "hello, 世界"

// String as byte slice
bytes := []byte(s)  // Copy conversion
str := string(bytes) // Back to string

// Rune slices for Unicode
runes := []rune(s)  // Full Unicode characters
sub := string(runes[7:9]) // "世界"

// String slicing
fmt.Println(s[0:5]) // "hello" (byte positions)

Important Notes:

  • String slices reference original string memory
  • Indexing returns bytes (not characters)
  • Use []rune for proper Unicode handling
  • Conversions between string and []byte copy data

Strings Quiz

What does s[0] return for a string?

  • The first character as string
  • The first byte as uint8
  • A rune value

7. Performance Tips

Optimizing slice usage can significantly impact application performance.

// Pre-allocate when size is known
result := make([]Item, 0, expectedSize)

// Reuse buffers
var buffer []byte
for _, task := range tasks {
    buffer = buffer[:0] // reset
    buffer = process(task, buffer)
}

// Avoid unnecessary allocations
func concat(a, b []string) []string {
    return append(a, b...)
}

// Capacity hints
names := make([]string, 0, len(users))
for _, u := range users {
    names = append(names, u.Name)
}

Optimization Strategies:

  • Pre-size slices when possible
  • Reuse buffers across operations
  • Minimize allocations in hot paths
  • Use capacity hints for append-heavy code
  • Consider arrays for small, fixed-size collections

Performance Quiz

When should you prefer arrays over slices?

  • When you need dynamic sizing
  • For small, fixed-size collections
  • When working with interfaces
0 Interaction
0 Views
Views
0 Likes
×
×
×
🍪 CookieConsent@Ptutorials:~

Welcome to Ptutorials

$ Allow cookies on this site ? (y/n)

top-home