Skip to content

Bulk Operations Guide

Bulk operations provide efficient methods for performing batch operations on collections. Instead of calling individual methods in loops, bulk operations process multiple elements in a single call, offering better performance and cleaner code.

Overview

All enhanced collection types in Harneet support bulk operations:

  • Stack: push_all(), pop_all()
  • Queue: enqueue_all(), dequeue_all()
  • Set: add_all(), remove_all(), contains_all()
  • LinkedList: add_all_first(), add_all_last(), add_all_at(), remove_range()
  • EnhancedArray: add_all(), insert_all_at(), contains_all(), remove_range()

Benefits

Performance

  • Reduced overhead: Single function call instead of loop iterations
  • Better memory allocation: Pre-allocate space for multiple elements
  • Optimized operations: Internal optimizations for batch processing
  • Cache efficiency: Better CPU cache utilization

Code Quality

  • Cleaner code: More readable and maintainable
  • Atomic operations: All-or-nothing semantics where applicable
  • Error handling: Detailed feedback on operation results
  • Type safety: Compile-time validation

Stack Bulk Operations

push_all(items array)

Pushes multiple items onto the stack at once.

1
2
3
4
5
6
import collections

var stack = collections.new_stack()
var items = [1, 2, 3, 4, 5]
stack.push_all(items)
// Stack now contains: [1, 2, 3, 4, 5] (5 on top)

Performance: O(n) where n is the number of items

pop_all(count int) → array

Pops multiple items from the stack at once, returning them in LIFO order.

1
2
3
4
5
var stack = collections.new_stack()
stack.push_all([1, 2, 3, 4, 5])
var popped = stack.pop_all(3)
// popped = [5, 4, 3] (LIFO order)
// stack now contains: [1, 2]

Performance: O(n) where n is the count
Note: If count exceeds stack size, returns all available items

Queue Bulk Operations

enqueue_all(items array)

Enqueues multiple items at once.

1
2
3
4
5
6
import collections

var queue = collections.new_queue()
var items = ["a", "b", "c", "d"]
queue.enqueue_all(items)
// Queue now contains: [a, b, c, d] (a at front)

Performance: O(n) where n is the number of items

dequeue_all(count int) → array

Dequeues multiple items at once, returning them in FIFO order.

1
2
3
4
5
var queue = collections.new_queue()
queue.enqueue_all([1, 2, 3, 4, 5])
var dequeued = queue.dequeue_all(3)
// dequeued = [1, 2, 3] (FIFO order)
// queue now contains: [4, 5]

Performance: O(n) where n is the count
Note: If count exceeds queue size, returns all available items

Set Bulk Operations

add_all(items array) → BulkResult

Adds multiple items to the set, returning detailed operation results.

1
2
3
4
5
6
7
8
9
import collections

var set = collections.new_set()
var items = [10, 20, 30, 20, 40]  // Note: 20 is duplicate
var result = set.add_all(items)

fmt.Printf("Success: %d, Failure: %d\n", 
    result.SuccessCount, result.FailureCount)
// Output: Success: 4, Failure: 1

Performance: O(n) average case where n is the number of items
Returns: BulkResult with SuccessCount and FailureCount

remove_all(items array) → BulkResult

Removes multiple items from the set.

1
2
3
4
5
6
7
var set = collections.new_set()
set.add_all([1, 2, 3, 4, 5])
var result = set.remove_all([2, 4, 6])  // 6 doesn't exist

fmt.Printf("Success: %d, Failure: %d\n", 
    result.SuccessCount, result.FailureCount)
// Output: Success: 2, Failure: 1

Performance: O(n) average case
Returns: BulkResult with operation feedback

contains_all(items array) → bool

Checks if the set contains all specified items.

1
2
3
4
5
var set = collections.new_set()
set.add_all([1, 2, 3, 4, 5])

var has_all = set.contains_all([1, 3, 5])  // true
var has_missing = set.contains_all([1, 3, 10])  // false

Performance: O(n) average case
Returns: true if all items are present, false otherwise

LinkedList Bulk Operations

add_all_first(items array)

Adds multiple items to the beginning of the list.

1
2
3
4
5
6
7
import collections

var list = collections.new_linked_list()
list.add_all_last([100, 200])
list.add_all_first([10, 20])
// List: [20, 10, 100, 200]
// Items added in reverse order

Performance: O(n) where n is the number of items
Note: Items are prepended in reverse order

add_all_last(items array)

Adds multiple items to the end of the list.

1
2
3
var list = collections.new_linked_list()
list.add_all_last([1, 2, 3, 4, 5])
// List: [1, 2, 3, 4, 5]

Performance: O(n) where n is the number of items

add_all_at(index int, items array)

Inserts multiple items at a specific position.

1
2
3
4
var list = collections.new_linked_list()
list.add_all_last([1, 2, 5, 6])
list.add_all_at(2, [3, 4])
// List: [1, 2, 3, 4, 5, 6]

Performance: O(m + n) where m is the index position and n is the number of items

remove_range(start int, end int) → array

Removes a range of elements (inclusive).

1
2
3
4
5
var list = collections.new_linked_list()
list.add_all_last([10, 20, 30, 40, 50])
var removed = list.remove_range(1, 3)
// removed = [20, 30, 40]
// list = [10, 50]

Performance: O(m + n) where m is the start index and n is the range size

EnhancedArray Bulk Operations

add_all(items array)

Appends multiple items to the array.

1
2
3
4
5
import collections

var array = collections.new_array()
array.add_all([1, 2, 3, 4, 5])
// Array: [1, 2, 3, 4, 5]

Performance: O(n) amortized where n is the number of items

insert_all_at(index int, items array)

Inserts multiple items at a specific position.

1
2
3
4
var array = collections.new_array()
array.add_all([1, 2, 5, 6])
array.insert_all_at(2, [3, 4])
// Array: [1, 2, 3, 4, 5, 6]

Performance: O(m + n) where m is elements to shift and n is items to insert

contains_all(items array) → bool

Checks if the array contains all specified items.

1
2
3
4
5
var array = collections.new_array()
array.add_all([1, 2, 3, 4, 5])

var has_all = array.contains_all([1, 3, 5])  // true
var has_missing = array.contains_all([1, 3, 10])  // false

Performance: O(n × m) where n is array size and m is items to check

remove_range(start int, end int) → array

Removes a range of elements (inclusive).

1
2
3
4
5
var array = collections.new_array()
array.add_all([10, 20, 30, 40, 50])
var removed = array.remove_range(1, 3)
// removed = [20, 30, 40]
// array = [10, 50]

Performance: O(n) where n is the number of elements to shift

Performance Comparison

Individual vs Bulk Operations

// ❌ Inefficient: Individual operations
var stack = collections.new_stack()
for i in range(1000) {
    stack.push(i)  // 1000 function calls
}

// ✅ Efficient: Bulk operation
var stack = collections.new_stack()
var items = []
for i in range(1000) {
    items = append(items, i)
}
stack.push_all(items)  // 1 function call

Performance Benefits

Operation Individual Bulk Improvement
Stack Push (1000 items) 1000 calls 1 call ~10x faster
Queue Enqueue (1000 items) 1000 calls 1 call ~10x faster
Set Add (1000 items) 1000 calls 1 call ~8x faster
List Append (1000 items) 1000 calls 1 call ~12x faster

Best Practices

1. Use Bulk Operations for Large Datasets

// ✅ Good: Use bulk operations for >10 items
var items = []
for i in range(100) {
    items = append(items, i)
}
stack.push_all(items)

// ❌ Avoid: Individual operations in loops
for i in range(100) {
    stack.push(i)
}

2. Pre-allocate Arrays

// ✅ Good: Build array first
var items = []
for i in range(1000) {
    items = append(items, process(i))
}
set.add_all(items)

// ❌ Avoid: Multiple small bulk operations
for i in range(100) {
    var batch = [i, i+1, i+2]
    set.add_all(batch)
}

3. Check BulkResult for Sets

1
2
3
4
5
6
7
8
// ✅ Good: Check operation results
var result = set.add_all(items)
if result.FailureCount > 0 {
    fmt.Printf("Warning: %d duplicates ignored\n", result.FailureCount)
}

// ❌ Avoid: Ignoring operation feedback
set.add_all(items)  // No feedback

4. Use Range Operations Wisely

1
2
3
4
5
6
7
// ✅ Good: Remove contiguous ranges
var removed = list.remove_range(10, 20)

// ❌ Avoid: Multiple single removals
for i in range(10, 21) {
    list.remove_at(10)  // Index shifts each time!
}

Error Handling

Empty Arrays

All bulk operations handle empty arrays gracefully:

1
2
3
var stack = collections.new_stack()
var empty = []
stack.push_all(empty)  // No-op, no error

Out of Bounds

Operations that exceed collection size return available items:

1
2
3
var stack = collections.new_stack()
stack.push_all([1, 2, 3])
var popped = stack.pop_all(10)  // Returns [3, 2, 1]

Invalid Indices

Range operations validate indices:

1
2
3
var list = collections.new_linked_list()
list.add_all_last([1, 2, 3])
// list.remove_range(5, 10)  // Error: index out of bounds

Common Patterns

Batch Processing

function process_batch(items array) {
    var results = collections.new_array()
    var processed = []

    for item in items {
        processed = append(processed, transform(item))
    }

    results.add_all(processed)
    return results
}

Set Operations

function union(set1, set2) {
    var result = collections.new_set()
    result.add_all(set1.to_array())
    result.add_all(set2.to_array())
    return result
}

function intersection(set1, set2) {
    var result = collections.new_set()
    var items = set1.to_array()

    for item in items {
        if set2.contains(item) {
            result.add(item)
        }
    }
    return result
}

Queue Batch Processing

function process_queue_batch(queue, batch_size int) {
    var batch = queue.dequeue_all(batch_size)
    var results = []

    for item in batch {
        results = append(results, process(item))
    }

    return results
}

Migration Guide

From Individual Operations

1
2
3
4
5
6
7
8
9
// Before
var stack = collections.new_stack()
for item in items {
    stack.push(item)
}

// After
var stack = collections.new_stack()
stack.push_all(items)

From Manual Loops

// Before
var set = collections.new_set()
var success_count = 0
for item in items {
    if set.add(item) {
        success_count = success_count + 1
    }
}

// After
var set = collections.new_set()
var result = set.add_all(items)
var success_count = result.SuccessCount

See Also