12 essential data structures for JavaScript developers. Each one explained with Big O complexity, animated visuals, and real code samples you can copy.
ArrayFixed-size, contiguous block of memory. Elements are stored sequentially and accessed by index in constant time. The foundation of most other data structures.
arr[0] = 7
Complexity
When to use
// Fixed-length array (typed - contiguous memory)
const buf = new Int32Array(5); // O(1) - fixed size
buf[0] = 42; // O(1) access
buf[3] = 99; // O(1) assign
// Iterate
for (const val of buf)
console.log(val);
// No resize - typed arrays are fixed
console.log(buf.length); // 5
const idx = buf.indexOf(99); // O(n) search -> 3
const sorted = buf.toSorted(); // O(n log n) - ES2023ArrayResizable array that automatically grows when capacity is exceeded. The most commonly used data structure in most languages. Doubles its internal storage when full, giving amortized O(1) appends.
arr[0] = 7
Complexity
When to use
// Array IS the dynamic array in JS
const nums = [10, 20, 30];
nums.push(40); // O(1) amortized - append
nums.push(50); // [10, 20, 30, 40, 50]
nums[1] = 25; // O(1) - index access
nums.splice(2, 0, 28); // O(n) - insert at index 2
nums.splice(1, 1); // O(n) - remove at index 1
const has = nums.includes(40); // O(n) -> true
const idx = nums.indexOf(28); // O(n) -> 1
nums.sort((a, b) => a - b); // O(n log n)
console.log(nums); // [10, 28, 30, 40, 50]Array (push/pop)Last-In-First-Out (LIFO) collection. Only the top element is accessible. Used for tracking state that must be unwound in reverse order.
Push 10
Complexity
When to use
// Use Array as a stack (LIFO)
const stack = [];
stack.push(10); // O(1)
stack.push(20);
stack.push(30);
const top = stack.at(-1); // O(1) peek -> 30
const val = stack.pop(); // O(1) -> 30
// Interview pattern: valid parentheses
function isValid(s) {
const st = [];
const map = { ")": "(", "]": "[", "}": "{" };
for (const c of s) {
if (!map[c]) st.push(c);
else if (st.pop() !== map[c]) return false;
}
return st.length === 0;
}Array (shift/push)First-In-First-Out (FIFO) collection. Elements are added at the back and removed from the front. Fundamental for breadth-first processing.
Enqueue 10
Complexity
When to use
// Array-based queue (shift is O(n) - fine for interviews)
const queue = [];
queue.push("A"); // O(1) enqueue
queue.push("B");
queue.push("C");
const front = queue[0]; // O(1) peek -> "A"
const next = queue.shift(); // O(n) dequeue -> "A"
// Interview pattern: BFS
function bfs(root) {
const q = [root];
while (q.length > 0) {
const node = q.shift(); // O(n)
console.log(node.val);
if (node.left) q.push(node.left);
if (node.right) q.push(node.right);
}
}MapMaps keys to values using a hash function for near-constant-time lookups. The single most important data structure for coding interviews. Every language has a built-in implementation.
hash("age") = 0
Complexity
When to use
// Map - keys can be any type
const map = new Map();
map.set("apple", 3); // O(1)
map.set("banana", 5);
map.set("apple", 10); // O(1) update
const val = map.get("apple"); // O(1) -> 10
const has = map.has("banana"); // O(1) -> true
map.delete("banana"); // O(1)
// Interview pattern: Two Sum
function twoSum(nums, target) {
const seen = new Map();
for (let i = 0; i < nums.length; i++) {
const need = target - nums[i];
if (seen.has(need)) return [seen.get(need), i];
seen.set(nums[i], i);
}
}SetUnordered collection of unique elements. Uses hashing internally for O(1) membership testing. Supports mathematical set operations like union, intersection, and difference.
hash("age") = 0
Complexity
When to use
const set = new Set([1, 2, 3, 4, 5]);
set.add(6); // O(1)
set.add(3); // O(1) - no duplicate added
set.delete(1); // O(1)
const has = set.has(4); // O(1) -> true
// Set operations (ES2025 - or polyfill)
const a = new Set([1, 2, 3]);
const b = new Set([2, 3, 4]);
const union = a.union(b); // {1, 2, 3, 4}
const inter = a.intersection(b); // {2, 3}
const diff = a.difference(b); // {1}
// Interview pattern: contains duplicate
const hasDup = (nums) => new Set(nums).size !== nums.length;manual implSequence of nodes where each node points to the next (singly linked) or both next and previous (doubly linked). Efficient insertion and deletion at any known position, but no index-based access.
traversing: 5
Complexity
When to use
// No built-in - manual implementation
class ListNode {
constructor(val, next = null) {
this.val = val;
this.next = next;
}
}
// Build: 1 -> 2 -> 3
let head = new ListNode(1, new ListNode(2, new ListNode(3)));
// Insert at head - O(1)
head = new ListNode(0, head); // 0 -> 1 -> 2 -> 3
// Interview pattern: reverse linked list
function reverse(head) {
let prev = null, curr = head;
while (curr) {
const next = curr.next; // O(1) per node
curr.next = prev;
prev = curr;
curr = next;
}
return prev;
}sorted ArrayCollection of unique elements maintained in sorted order, typically backed by a balanced binary search tree (red-black tree). Supports range queries and O(log n) min/max.
search(8)
Complexity
When to use
// No built-in sorted set - use a sorted array approach
// For interviews, sort + binary search is typical
const sorted = [1, 3, 5, 8, 9];
// Binary search helper - O(log n)
function bisect(arr, val) {
let lo = 0, hi = arr.length;
while (lo < hi) {
const mid = (lo + hi) >> 1;
if (arr[mid] < val) lo = mid + 1;
else hi = mid;
}
return lo;
}
// Insert maintaining order - O(n) for shift
const pos = bisect(sorted, 4);
sorted.splice(pos, 0, 4); // [1, 3, 4, 5, 8, 9]
const min = sorted[0]; // O(1)
const max = sorted[sorted.length - 1]; // O(1)sorted MapKey-value pairs maintained in sorted key order, typically backed by a balanced BST. Enables ordered iteration and range lookups that hash maps cannot provide.
search(8)
Complexity
When to use
// No built-in sorted map - use sorted array of entries
// For interviews, sort keys or use a Map + sort when needed
const map = new Map();
map.set("banana", 2);
map.set("apple", 5);
map.set("cherry", 1);
// Iterate in sorted key order
const sortedEntries = [...map.entries()]
.sort(([a], [b]) => a.localeCompare(b));
for (const [key, val] of sortedEntries)
console.log(`${key}: ${val}`);
// apple: 5, banana: 2, cherry: 1
// For frequent sorted access, maintain a sorted keys array
// or use a third-party balanced BST librarymanual MinHeapCollection where elements are dequeued by priority rather than insertion order. Typically implemented as a binary heap. Essential for shortest-path algorithms and top-K problems.
min-heap
Complexity
When to use
// No built-in - minimal binary heap for interviews
class MinHeap {
#data = [];
push(val) { // O(log n)
this.#data.push(val);
this.#bubbleUp(this.#data.length - 1);
}
pop() { // O(log n)
const top = this.#data[0];
const last = this.#data.pop();
if (this.#data.length) { this.#data[0] = last; this.#sinkDown(0); }
return top;
}
peek() { return this.#data[0]; } // O(1)
get size() { return this.#data.length; }
#bubbleUp(i) {
while (i > 0) { const p = (i-1)>>1; if (this.#data[p]<=this.#data[i]) break; [this.#data[p],this.#data[i]]=[this.#data[i],this.#data[p]]; i=p; }
}
#sinkDown(i) {
const n=this.#data.length; while(true) { let s=i,l=2*i+1,r=2*i+2; if(l<n&&this.#data[l]<this.#data[s])s=l; if(r<n&&this.#data[r]<this.#data[s])s=r; if(s===i)break; [this.#data[s],this.#data[i]]=[this.#data[i],this.#data[s]]; i=s; }
}
}N/A (single-threaded)Thread-safe hash map designed for concurrent read/write access from multiple threads. Uses fine-grained locking or lock-free techniques instead of a single global lock.
hash("age") = 0
Complexity
When to use
// JS is single-threaded - no ConcurrentDictionary needed
// For SharedArrayBuffer workers, use Atomics:
// Main thread
const sab = new SharedArrayBuffer(1024);
const view = new Int32Array(sab);
// Worker thread - atomic operations
Atomics.store(view, 0, 42); // O(1) thread-safe write
const val = Atomics.load(view, 0); // O(1) thread-safe read
// Compare-and-swap (lock-free pattern)
Atomics.compareExchange(view, 0, 42, 100);
// For key-value: use a regular Map
// JS event loop guarantees no data races in single-threadTypedArrayZero-copy view over a contiguous region of memory. Lets you reference a portion of an array or buffer without allocating new memory. Critical for performance-sensitive parsing and processing.
Span[0..3] = [1, 2, 3]
Complexity
When to use
// TypedArray = contiguous memory view (like Span)
const buffer = new ArrayBuffer(16);
const view = new Float64Array(buffer); // O(1) view
view[0] = 3.14;
view[1] = 2.72;
// Subarray - zero-copy slice, shares memory
const slice = view.subarray(0, 1); // O(1)
slice[0] = 99.9; // mutates original
// DataView for mixed types on same buffer
const dv = new DataView(buffer);
dv.setInt8(0, 127); // O(1)
const byte = dv.getInt8(0); // O(1) -> 127
// ArrayBuffer.transfer for resize (ES2024)
const bigger = buffer.transfer(32); // O(n) copyAverage-case time complexity. * = amortized.
| Structure | Access | Search | Insert | Delete |
|---|---|---|---|---|
| Array | O(1) | O(n) | O(n) | O(n) |
| Dynamic Array | O(1) | O(n) | O(1)* | O(n) |
| Stack | O(n) | O(n) | O(1)* | O(1) |
| Queue | O(n) | O(n) | O(1)* | O(1) |
| Hash Map | O(1) | O(1) | O(1)* | O(1) |
| Hash Set | N/A | O(1) | O(1)* | O(1) |
| Linked List | O(n) | O(n) | O(1) | O(1) |
| Sorted Set | O(n) | O(log n) | O(log n) | O(log n) |
| Sorted Map | O(log n) | O(log n) | O(log n) | O(log n) |
| Priority Queue | O(n) | O(n) | O(log n) | O(log n) |
| Concurrent Map | O(1) | O(1) | O(1)* | O(1) |
| Memory View | O(1) | O(n) | N/A | N/A |
| I need to... | Use |
|---|---|
| Store items by index, resize dynamically | List / Dynamic Array |
| Map keys to values with O(1) lookup | HashMap / Dictionary |
| Track unique items, check existence in O(1) | HashSet / Set |
| Last-in-first-out (undo, DFS, brackets) | Stack |
| First-in-first-out (BFS, task queues) | Queue |
| Keep elements sorted at all times | SortedSet / TreeSet |
| Process items by priority (Dijkstra, top-K) | PriorityQueue / Heap |
| Insert/delete at a known position in O(1) | LinkedList |
| Sorted key-value pairs | SortedDictionary / TreeMap |
| Thread-safe shared cache | ConcurrentDictionary |
| Slice arrays/strings without copying | Span / Slice / memoryview |
The most commonly used are dynamic arrays (List/ArrayList/vector), hash maps (Dictionary/HashMap/dict), and hash sets. For interviews, also know stacks, queues, trees, and priority queues. These cover 90%+ of coding interview problems.
Start with the dynamic array and hash map. Together they solve the majority of interview problems. Then learn stacks (for DFS, bracket matching) and queues (for BFS). After that, tackle trees, heaps, and graphs.
No. Big O measures algorithmic complexity, not language-specific performance. A hash map lookup is O(1) whether you use Python dict, Java HashMap, or C# Dictionary. Constant factors differ (C++ is faster than Python in wall-clock time), but Big O is the same.
It depends on the language runtime. Check the JavaScript standard library documentation for heap or priority queue support.
Practice with an AI interviewer that asks you to implement, optimize, and explain data structure choices in real time.
Try a free mock interviewarrow_forward