Dynamic Arrays: How They Work

Learn Dynamic Arrays: How They Work with practical examples, explanations, best practices, and real-world coding techniques.

What Is a Dynamic Array?

A dynamic array is an array-based data structure that can automatically grow or shrink as elements are added or removed. Unlike a fixed-size array, a dynamic array does not require the programmer to know the final number of elements when the structure is created.

Dynamic arrays are commonly implemented using a regular array as their underlying storage. When the existing storage becomes full, a larger array is allocated and the existing elements are copied into it.

Why Are Dynamic Arrays Needed?

A traditional fixed-size array requires its capacity to be determined when the array is allocated. This can be inconvenient when the number of elements changes during program execution.

Fixed Array Dynamic Array
Capacity is generally fixed. Capacity can grow when required.
Resizing requires creating another structure. Resizing can be handled internally.
Useful when the required size is known. Useful when the size changes during execution.

Size vs Capacity

Two concepts are important when understanding dynamic arrays: size and capacity.

  • Size is the number of elements currently stored.
  • Capacity is the amount of storage currently available.
Values:

[10, 20, 30, 40, _, _, _, _]

Size     = 4
Capacity = 8

The unused positions represent available capacity. New elements can be added without immediately allocating another array until that capacity has been exhausted.

How Dynamic Array Resizing Works

Suppose a dynamic array has a capacity of four and all four positions are occupied.

Before resizing:

[10, 20, 30, 40]

Size     = 4
Capacity = 4

When another element is inserted, the implementation can allocate a larger array, copy the existing elements, and continue the insertion.

After resizing:

[10, 20, 30, 40, 50, _, _, _]

Size     = 5
Capacity = 8

A common strategy is to increase capacity by a growth factor, such as doubling it. The exact strategy depends on the language and implementation.

Steps Performed During Resizing

  1. Detect that the current storage is full.
  2. Allocate a larger underlying array.
  3. Copy the existing elements into the new array.
  4. Replace the old storage with the new storage.
  5. Insert the new element.

The copying step is what makes an individual resize operation expensive. However, resizing does not happen on every insertion when capacity grows geometrically.

Implementing a Dynamic Array in JavaScript

JavaScript's built-in Array already provides dynamic array behavior. However, implementing a simplified version helps demonstrate how resizing works internally.

class DynamicArray {
  constructor(capacity = 2) {
    this.data = new Array(capacity);
    this.size = 0;
  }

  push(value) {
    if (this.size === this.data.length) {
      this.resize();
    }

    this.data[this.size] = value;
    this.size++;
  }

  resize() {
    const newCapacity = this.data.length * 2;
    const newData = new Array(newCapacity);

    for (let i = 0; i < this.size; i++) {
      newData[i] = this.data[i];
    }

    this.data = newData;
  }

  get(index) {
    if (index < 0 || index >= this.size) {
      throw new Error("Index out of bounds");
    }

    return this.data[index];
  }
}

const numbers = new DynamicArray();

numbers.push(10);
numbers.push(20);
numbers.push(30);
numbers.push(40);

console.log(numbers.get(2));
// 30

console.log(numbers.data.length);
// 8

Appending Elements to a Dynamic Array

Adding an element to the end of a dynamic array is normally efficient. When unused capacity is available, the new value can be placed directly at the next available position.

const values = [10, 20, 30];

values.push(40);

console.log(values);
// [10, 20, 30, 40]

If the underlying storage needs to grow, the insertion may temporarily require copying existing elements into a larger storage area.

Dynamic Array Insertion at an Index

Inserting an element into the middle of a dynamic array still requires shifting the elements after the insertion position.

const values = [10, 20, 40, 50];

values.splice(2, 0, 30);

console.log(values);
// [10, 20, 30, 40, 50]

Dynamic resizing solves the capacity problem, but it does not eliminate the cost of shifting elements during insertion.

Removing Elements from a Dynamic Array

Elements can also be removed from a dynamic array. Removing an element from the end generally avoids shifting other elements, while removing from the beginning or middle can require elements to be moved.

const values = [10, 20, 30, 40];

values.pop();

console.log(values);
// [10, 20, 30]

Some dynamic-array implementations also reduce capacity when the array becomes sufficiently empty. Shrinking is normally done carefully to avoid repeatedly resizing the storage when elements are added and removed around the same size.

Amortized Time Complexity

One of the most important concepts associated with dynamic arrays is amortized complexity.

A resize operation can take O(n)

As a result, appending n elements has an overall O(n) cost, giving an O(1) amortized cost per append.

Operation Typical Complexity
Access by index O(1)
Append O(1) amortized
Append during resize O(n)
Insert at beginning O(n)
Insert in middle O(n)
Delete from end O(1) typical
Delete from beginning O(n)

Why Geometric Growth Is Useful

Consider a dynamic array that increases its capacity by one every time it becomes full. Adding many elements would cause repeated allocations and repeated copying of existing values.

Increasing capacity by a growth factor, such as doubling it, provides more unused space after a resize. This reduces how frequently expensive resize operations occur.

Capacity growth:

2 → 4 → 8 → 16 → 32 → 64

The exact growth policy is implementation-dependent, but geometric growth is a common strategy for achieving efficient amortized insertion.

Dynamic Arrays in JavaScript

JavaScript arrays are designed to be flexible and can grow or shrink as elements are added or removed. Developers normally use methods such as push(), pop(), splice(), and shift() instead of implementing resizing manually.

const users = [];

users.push("Asha");
users.push("Rahul");
users.push("Priya");

console.log(users);
// ["Asha", "Rahul", "Priya"]

users.push("Arun");

console.log(users.length);
// 4

Although JavaScript hides the underlying storage management, the dynamic array concept remains important for understanding how array-backed collections behave.

Dynamic Arrays vs Linked Lists

Both dynamic arrays and linked lists can represent collections whose size changes during execution, but they use different storage strategies.

Feature Dynamic Array Linked List
Index access O(1) O(n)
Append O(1) amortized O(1) with tail reference
Middle insertion O(n) O(1) after reaching the position
Memory layout Array-backed storage Node-based storage
Memory overhead Generally lower per element Extra node references

Advantages of Dynamic Arrays

  • Can grow as the number of elements increases.
  • Provides constant-time indexed access.
  • Supports efficient amortized appending.
  • Uses a simple array-backed representation.
  • Works well for collections where elements are frequently accessed by index.
  • Usually provides good cache locality compared with pointer-heavy structures.

Limitations of Dynamic Arrays

  • Resizing can temporarily require copying many elements.
  • Insertion near the beginning or middle requires shifting elements.
  • Unused capacity can consume additional memory.
  • Frequent resizing can be expensive if the growth strategy is poorly designed.
  • Removing elements does not always immediately reduce allocated capacity.

Common Dynamic Array Mistakes

  • Confusing the number of stored elements with the allocated capacity.
  • Resizing the array on every insertion.
  • Forgetting to copy all existing elements during a resize.
  • Using an inefficient growth strategy.
  • Assuming every append operation has a strict worst-case O(1) cost.
  • Ignoring the cost of shifting elements during middle insertion.
  • Creating shared nested references when building arrays of arrays.

When Should You Use a Dynamic Array?

Dynamic arrays are a strong choice when the collection size changes over time and the application frequently needs indexed access. They are particularly useful for lists of users, products, messages, results, records, and other sequential data.

If the application frequently inserts or removes elements from the middle of a collection, another data structure may provide better characteristics depending on the access pattern.

Key Takeaways

  • A dynamic array can grow as more elements are added.
  • Size represents the number of stored elements, while capacity represents available storage.
  • When capacity is exhausted, a larger underlying array can be allocated.
  • Existing elements must be copied during a resize.
  • Appending is typically O(1) amortized when geometric growth is used.
  • Insertion and deletion near the beginning or middle can require shifting elements.
  • JavaScript arrays provide dynamic array behavior without requiring manual resizing.
  • Dynamic arrays provide fast indexed access and are useful for many sequential collections.

Code Examples

class DynamicArray {
  constructor(capacity = 2) {
    this.data = new Array(capacity);
    this.size = 0;
  }

  push(value) {
    if (this.size === this.data.length) {
      this.resize();
    }

    this.data[this.size] = value;
    this.size++;
  }

  resize() {
    const newData = new Array(this.data.length * 2);

    for (let i = 0; i < this.size; i++) {
      newData[i] = this.data[i];
    }

    this.data = newData;
  }

  get(index) {
    if (index < 0 || index >= this.size) {
      throw new Error("Index out of bounds");
    }

    return this.data[index];
  }
}

const numbers = new DynamicArray();

numbers.push(10);
numbers.push(20);
numbers.push(30);

console.log(numbers.get(2));
// 30
    
const values = [];

for (let i = 1; i <= 10; i++) {
  values.push(i * 10);
}

console.log(values);
// [10, 20, 30, 40, 50, 60, 70, 80, 90, 100]

console.log(values.length);
// 10
    
const values = [10, 20, 40, 50];

values.splice(2, 0, 30);

console.log(values);
// [10, 20, 30, 40, 50]

values.splice(3, 1);

console.log(values);
// [10, 20, 30, 50]
    

Pro Tip

When analyzing a dynamic array, distinguish between the cost of one resize and the amortized cost of many appends. A resize can take O(n), but geometric capacity growth makes appending O(1) amortized over a long sequence of insertions.

🎨 IgKnight Docs

Master HTML, CSS, JS, Python, .NET and more with easy-to-follow lessons and practical code examples.

© 2025 IgKnight Learning Platform. All rights reserved.