1. go
  2. /data structures

Understanding Data Structures in Go Programming

Go provides a rich set of built-in data structures that help you organize and manage data efficiently. This guide covers all the essential data structures and their usage patterns.

Core Data Structures

  1. Arrays

    • Fixed-size sequences
    • Multi-dimensional arrays
    • Array operations
    • Performance characteristics
  2. Slices

    • Dynamic arrays
    • Slice operations
    • Memory management
    • Common patterns
  3. Maps

    • Key-value storage
    • Map operations
    • Concurrent access
    • Performance considerations
  4. Structs

    • Custom data types
    • Field tags
    • Methods
    • Embedding
  5. Pointers

    • Memory addresses
    • Pointer operations
    • Pass by reference
    • Pointer receivers
  6. Interfaces

    • Type abstraction
    • Interface satisfaction
    • Empty interface
    • Type assertions

Advanced Topics

Common Use Cases

Here are some typical scenarios for each data structure:

// Arrays for fixed-size data
var buffer [1024]byte

// Slices for dynamic data
numbers := make([]int, 0, 10)
numbers = append(numbers, 1, 2, 3)

// Maps for lookups
users := map[string]User{
    "alice": {Name: "Alice", Age: 30},
    "bob":   {Name: "Bob", Age: 25},
}

// Structs for complex data
type User struct {
    ID      string
    Name    string
    Address Address
}

// Interfaces for abstraction
type Reader interface {
    Read(p []byte) (n int, err error)
}

Best Practices

When working with data structures:

  1. Choose the Right Structure

    • Use arrays for fixed-size data
    • Use slices for dynamic collections
    • Use maps for key-value relationships
    • Use structs for complex data types
    • Use interfaces for abstraction
  2. Memory Management

    • Pre-allocate when size is known
    • Use capacity hints for slices
    • Clear references when done
    • Be aware of memory layout
  3. Performance Considerations

    • Consider access patterns
    • Understand memory allocation
    • Use benchmarking
    • Profile your code

Common Patterns

1. Stack Implementation

type Stack struct {
    items []interface{}
}

func (s *Stack) Push(item interface{}) {
    s.items = append(s.items, item)
}

func (s *Stack) Pop() interface{} {
    if len(s.items) == 0 {
        return nil
    }
    item := s.items[len(s.items)-1]
    s.items = s.items[:len(s.items)-1]
    return item
}

2. Safe Map Access

func GetUser(users map[string]User, id string) (User, bool) {
    user, exists := users[id]
    return user, exists
}

3. Generic Container

type Container[T any] struct {
    items []T
}

func (c *Container[T]) Add(item T) {
    c.items = append(c.items, item)
}

Performance Tips

  1. Slice Operations

    // Pre-allocate slices
    data := make([]int, 0, expectedSize)
    
    // Avoid copying large slices
    slice = append(slice[:i], slice[i+1:]...)
    
  2. Map Operations

    // Pre-allocate maps
    users := make(map[string]User, expectedSize)
    
    // Delete vs Clear
    users = make(map[string]User) // Clear all entries
    
  3. Struct Alignment

    // Good alignment
    type AlignedStruct struct {
        ID   int64  // 8 bytes
        Name string // 16 bytes
        Age  int32  // 4 bytes
        // 4 bytes padding
    }
    

Next Steps

After mastering data structures:

  1. Learn about concurrency patterns
  2. Explore the standard library
  3. Study algorithms
  4. Practice with testing

Additional Resources