Data structures — overview

Status: 🟩 COMPLETE Last updated: 2026-06-19 Plain-English tagline: The shapes data takes in memory — arrays, objects, maps, sets, stacks, queues, trees. Each one is good at SOME operations and bad at others; picking the right shape makes code fast and clear.


In plain English

Programs store data. The question is HOW. If you have 100 user records, where do they go? Just thrown together in a pile? In a sorted list? In a lookup table keyed by ID?

A data structure is a way of organizing data so specific operations are fast. Different structures excel at different things:

  • “Find the user with ID 47” — a map / hash table is amazing (constant time)
  • “Find the 10th user” — an array is amazing (constant time)
  • “Find users in alphabetical order” — a sorted array or tree is amazing
  • “Add an item, then take whatever I added most recently” — a stack fits
  • “Add an item, then take whatever I added earliest” — a queue fits
  • “Is X in this list of 1 million items?” — a set is amazing (constant time)

Choosing the right data structure is one of the highest-leverage decisions in code. It’s the difference between code that runs in milliseconds and code that takes minutes.

JavaScript provides most data structures built-in. You don’t write them from scratch; you choose which to use. This entry covers what each is for.

For Big-O performance analysis, see Time & space complexity. For algorithms that operate on these structures, see Algorithms — intro.


Why it matters

Three concrete reasons even a non-coder benefits from data structure literacy:

  1. AI-generated code uses them constantly. When Claude writes new Map() or new Set(), knowing what’s there makes the code readable instead of mysterious.

  2. “Why is this slow?” often has a data-structure answer. Looking up by ID in an array (slow — O(n)) vs in a map (fast — O(1)) is the same code with different speeds.

  3. They give vocabulary for thinking about problems. “I have a list of things; I want to deduplicate and check membership fast” → a Set. Spotting the right structure is half of solving the problem.

The trade-off: there are dozens of data structures. The 7 in this entry cover 95% of webapp work. The rest (B-trees, tries, bloom filters, etc.) are specialized.


The big 7

1. Array (a list)

A sequence of items, indexed by position. JavaScript’s Array is the universal “ordered collection.”

const fruits = ["apple", "banana", "cherry"];
fruits[0];               // "apple" — constant-time access by index
fruits.push("date");     // add to end — fast
fruits.unshift("plum");  // add to start — slow (shifts everything)
fruits.length;           // 5
fruits.includes("kiwi"); // false — but this is SLOW (checks each)

Best for: ordered data, accessing by position, iterating in order, stacks/queues.

Bad for: finding a specific item by content (linear scan), removing from middle (everything shifts).

In JavaScript: [] literal or new Array().

2. Object / Hash map

Keys mapped to values. JavaScript objects work as maps, but Map is a better-typed version.

const userById = {
  47: { name: "George" },
  92: { name: "Alice" },
};
 
userById[47];     // { name: "George" } — constant-time access by key
"47" in userById; // true
delete userById[47];
Object.keys(userById);   // ["92"]

Or the more flexible Map:

const userMap = new Map();
userMap.set(47, { name: "George" });
userMap.get(47);              // { name: "George" }
userMap.has(47);              // true
userMap.size;                  // 1
userMap.delete(47);

Best for: lookup by key (very fast), unique key constraint, “given X, find Y” relationships.

Bad for: ordered iteration (objects technically preserve insertion order in modern JS, but Map is better; sets / arrays are clearer for ordered).

Map vs Object:

ObjectMap
KeysStrings + Symbols onlyAny type (including objects)
SizeObject.keys(o).lengthm.size
IterationObject.entries(o)m.entries()
PerformanceOptimized for property-style accessOptimized for many adds/removes
Prototype pollution riskYesNo

For data with lots of dynamic keys: prefer Map. For struct-like records: prefer object.

3. Set

A collection of UNIQUE items. No duplicates allowed.

const seen = new Set();
seen.add("hello");
seen.add("hello");    // no effect; already there
seen.has("hello");    // true
seen.size;             // 1

Best for: deduplication, fast membership checking, set operations (union, intersection).

Bad for: ordered access by position (no indices).

A common pattern:

const ids = users.map(u => u.id);
const uniqueIds = [...new Set(ids)];     // deduplicate

4. Stack (LIFO — Last In, First Out)

Like a stack of plates. Add to the top; remove from the top. The last item you added is the first one you remove.

JavaScript doesn’t have a Stack class; you use an array with push and pop:

const stack = [];
stack.push("a");
stack.push("b");
stack.push("c");
stack.pop();          // "c"
stack.pop();          // "b"

Best for: undo history, function call tracking, parsing expressions, depth-first traversal.

Real-world examples:

  • Browser back button — a stack of pages
  • Undo in an editor — a stack of operations
  • Function calls — the “call stack” you see in stack traces

5. Queue (FIFO — First In, First Out)

Like a line at a coffee shop. Add to the back; remove from the front.

const queue = [];
queue.push("a");      // add to back
queue.push("b");
queue.shift();        // "a" — remove from front

Note: shift() on a JS array is O(n) — slow for large queues. For high-performance queues, use a real linked-list-based queue or process in batches.

Best for: task queues, BFS traversal, scheduled work, fairness (“first come first served”).

Real-world examples:

  • Print queue
  • Background job queue
  • HTTP request queue (Vercel functions, browser fetch concurrency limits)

6. Linked list

A chain of nodes, each pointing to the next. Allows fast inserts/removes anywhere; slow access by index.

JavaScript doesn’t ship one; rarely needed in web work because arrays handle 99% of cases. Worth knowing the name because algorithm books reference them constantly.

[A] → [B] → [C] → [D] → null

Best for: very specific cases where you frequently insert/remove in the MIDDLE of a sequence.

Almost always: use an Array instead.

7. Tree

Nodes with PARENT-CHILD relationships. The HTML DOM is a tree. Git history is a tree. The filesystem is a tree.

        root
       /    \
     A        B
    / \      / \
   C   D    E   F

Specialized trees include:

  • Binary search tree — efficient sorted-data lookup
  • B-tree — what databases use for indexes
  • Trie — efficient string prefix lookup (autocomplete)
  • Red-black tree, AVL tree — self-balancing variants

For webapp work: you’ll DEAL with trees (React component tree, JSON nested objects, file paths) but rarely IMPLEMENT one. They appear naturally in the data; you don’t construct them.


A concrete example: choosing the right structure

Task: “Given a list of 10,000 user records, build a UI showing each user’s name. When the user clicks, show the user’s friends. Friends are referenced by ID.”

// Bad — repeated linear search for friends
const users = [/* 10,000 users */];
function getFriends(userId) {
  return users.filter(u => u.friends.includes(userId));
  // O(n²) — 10k × 10k = 100M operations
}
 
// Good — build a map once, lookups are constant
const userById = new Map(users.map(u => [u.id, u]));
function getFriends(userId) {
  const user = userById.get(userId);
  return user.friends.map(fid => userById.get(fid));
  // O(n) for map build; O(1) per lookup
}

For 10,000 users: the bad version takes minutes; the good version takes milliseconds. SAME logic, different data structure choices.

This is the kind of decision data-structure literacy unlocks.


Common operations and their typical performance

For each structure, the most-used operations and their Big-O:

StructureAccess by indexFind by valueAdd to endAdd to frontRemove
ArrayO(1)O(n)O(1)O(n)O(n)
Map / ObjectN/AO(1) by keyO(1)N/AO(1)
SetN/AO(1)O(1)N/AO(1)
StackO(1) (top only)O(n)O(1)N/AO(1) (top only)
QueueO(1) (front only)O(n)O(1)O(n)O(1) (front only)
Linked ListO(n)O(n)O(1) (if you track tail)O(1)O(1) (if you have the node)

The key insight: Map / Set lookups by key are O(1). This is what makes them so valuable.


Common gotchas

  • JavaScript object keys are strings (with rare exceptions). obj[123] and obj["123"] are the same key. To use objects as keys: use Map.

  • forEach and .map() on arrays are fine for small data. For 10M items, for loops are 2-3× faster (no closure overhead).

  • array.includes() is O(n). For large arrays, convert to a Set first.

  • Object.keys() returns strings ONLY. Even if you set obj[42], Object.keys(obj) returns ["42"]. Use Object.entries() to get key + value pairs.

  • Spread copies are SHALLOW. [...arr] copies the outer array; inner objects are still references. For deep copies, use structuredClone(arr).

  • Set preserves insertion order. Iterating a Set gives items in the order they were added.

  • Map preserves insertion order. Same.

  • Object preserves insertion order for string keys (ES2015+). For mixed keys, order is more complex.

  • Linked lists in JS are rare but pop up. When you see next: { value: X, next: { ... } }, you’re looking at one.

  • The DOM is a tree. Every HTML element has a parent (except the root) and zero-or-more children. CSS selectors traverse it.

  • Recursive data structures need recursive code. A tree’s traversal is typically recursive. See Recursion.

  • JSON.stringify(map) returns "{}". Maps don’t serialize to JSON. Convert: JSON.stringify([...map]) or build a plain object.

  • TypeScript distinguishes Record<string, T> from Map<string, T>. Different operations. Pick deliberately.

  • Comparing arrays / objects by === checks reference equality. [1,2,3] === [1,2,3] is FALSE. For value equality, compare contents (or use a library).

  • array.indexOf(NaN) returns -1 — NaN is not equal to itself. Use array.findIndex(x => Number.isNaN(x)).

  • Array.prototype.length is settable. arr.length = 0 empties the array. Useful but surprising.

  • Array(5) and new Array(5) create a sparse array of length 5, not [5]. Use Array.of(5) for the latter.

  • array.sort() mutates the array. Returns the same array sorted. Use [...arr].sort() for a copy.

  • array.sort() without arguments sorts by string conversion. [10, 2, 30].sort() gives [10, 2, 30] (string sort!). For numeric: arr.sort((a, b) => a - b).

  • Modifying a collection while iterating is unsafe. A for loop over an array you’re removing from will skip items or crash. Iterate in reverse, or build a separate “to remove” list.

  • WeakMap / WeakSet exist. Same as Map/Set, but keys are weakly-held (garbage collected when no other references exist). Used for caches that shouldn’t prevent garbage collection.

  • Array.from(...) and spread work on iterables. Array.from(new Set([1, 2, 3])) or [...new Set([1, 2, 3])] both give [1, 2, 3].

  • JavaScript arrays are not strictly arrays. Under the hood, V8 sometimes implements them as hash tables (for sparse arrays) or arrays (for dense ones). Performance depends on usage.

  • TypedArrays exist for binary data. Uint8Array, Float32Array, etc. Fixed-size, fixed-type, more memory-efficient. Used for image data, audio, network protocols.

  • Object.freeze(obj) makes it shallowly immutable. Inner objects can still be mutated. For deep immutability, use a library (Immer, Immutable.js) or structuredClone + freeze recursively.

  • AI-generated code may default to arrays where Sets/Maps would be faster. When reviewing, ask: “is this a lookup? does it need to be unique?” If yes to either, suggest the right structure.

  • The hardest part of data structures is RECOGNIZING the right one. Once you spot “this is a queue,” the implementation is mechanical.


What you don’t need to memorize

Knowing the NAMES and WHAT THEY’RE FOR is enough. You almost never:

  • Implement them from scratch (JavaScript ships them)
  • Pick exotic ones (B-trees, tries, splay trees) — those are for database/library authors
  • Optimize at the data-structure level early — profile first, then change

The skill is recognizing the right structure for the job, not implementing them. Practice by reading code: every Map, Set, Array you see, ask “WHY this one?”


See also


Sources