main.go
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
package main

import (	"log"
	"strconv"
	"time")

// ZeroAllocFilterInt filters an int slice with zero heap allocations (pre-allocates output)
// Predicate returns true if element should be included in the result
func ZeroAllocFilterInt(input []int, predicate func(int) bool) []int {
	// Pre-allocate output slice with capacity equal to input (worst case: all elements are kept)
	output := make([]int, 0, len(input))
	for _, v := range input {
		if predicate(v) {
			output = append(output, v)
		}
	}
	// Optional: shrink capacity to fit length (avoids memory waste)
	return append([]int(nil), output...)
}

// ZeroAllocMapIntToString maps an int slice to a string slice with minimal allocations
// Transformer converts an int to a string
func ZeroAllocMapIntToString(input []int, transformer func(int) string) []string {
	// Pre-allocate output slice with exact length (no reallocations)
	output := make([]string, len(input))
	for i, v := range input {
		output[i] = transformer(v)
	}
	return output
}

// ZeroAllocReduceInt reduces an int slice to a single int value
// Reducer takes accumulator and current element, returns new accumulator
func ZeroAllocReduceInt(input []int, initial int, reducer func(int, int) int) int {
	acc := initial
	for _, v := range input {
		acc = reducer(acc, v)
	}
	return acc
}

// ZeroAllocFilterMap combines filter and map in one pass (even more efficient)
func ZeroAllocFilterMapIntToFloat(input []int, predicate func(int) bool, transformer func(int) float64) []float64 {
	// First pass: count elements that pass predicate to pre-allocate exact capacity
	count := 0
	for _, v := range input {
		if predicate(v) {
			count++
		}
	}

	// Second pass: filter and transform
	output := make([]float64, 0, count)
	for _, v := range input {
		if predicate(v) {
			output = append(output, transformer(v))
		}
	}
	return output
}

// Example Usage
func main() {
	// Sample input slice
	numbers := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}

	// 1. Filter even numbers (zero allocation)
	evens := ZeroAllocFilterInt(numbers, func(n int) bool {
		return n%2 == 0
	})
	log.Printf("Even numbers: %v (len: %d, cap: %d)", evens, len(evens), cap(evens))

	// 2. Map ints to strings (zero allocation growth)
	stringNumbers := ZeroAllocMapIntToString(numbers, func(n int) string {
		return strconv.Itoa(n)
	})
	log.Printf("String numbers: %v", stringNumbers)

	// 3. Reduce to sum of all numbers
	sum := ZeroAllocReduceInt(numbers, 0, func(acc, n int) int {
		return acc + n
	})
	log.Printf("Sum of numbers: %d", sum)

	// 4. Filter and map in one pass (optimal for large slices)
	filteredFloats := ZeroAllocFilterMapIntToFloat(numbers, func(n int) bool {
		return n > 5
	}, func(n int) float64 {
		return float64(n) * 1.5
	})
	log.Printf("Numbers >5 multiplied by 1.5: %v", filteredFloats)

	// Benchmark example (compare with standard slice operations)
	largeSlice := make([]int, 1_000_000)
	for i := range largeSlice {
		largeSlice[i] = i + 1
	}

	start := time.Now()
	_ = ZeroAllocFilterInt(largeSlice, func(n int) bool { return n%3 == 0 })
	duration := time.Since(start)
	log.Printf("Filtered 1M elements in %v (zero allocation)", duration)
}

How It Works

Performance-focused slice helpers that avoid extra allocations by pre-sizing outputs and reusing buffers.

Implements filter, map, and reduce operations that allocate once based on input length, reuse underlying arrays, and avoid garbage collector churn while iterating.

Key Concepts

  • 1Preallocated output slices minimize GC pressure.
  • 2Functions accept predicates and mappers but keep tight loops.
  • 3Includes a reduce helper for aggregated calculations without copies.

When to Use This Pattern

  • High-throughput data processing in latency-sensitive services.
  • Performance-critical code paths like parsers or log pipelines.
  • Hot loops in real-time analytics where allocations add jitter.

Best Practices

  • Benchmark with real data to ensure gains outweigh complexity.
  • Avoid returning subslices that keep large backing arrays alive.
  • Document ownership of returned slices to prevent unintended reuse.
Go Version1.14
Difficultyintermediate
Production ReadyYes
Lines of Code103