MODULE 1

Python — Core Idioms

Comprehensions, generators, context managers, decorators.

Comprehensions

# List, set, dict, generator comprehensions
squares = [x*x for x in range(10) if x % 2 == 0]
uniq   = {w.lower() for w in words}
by_id  = {u.id: u for u in users}
lazy   = (x*x for x in big)          # generator, O(1) memory
flat   = [c for row in matrix for c in row]   # flatten (nested for)
mat3x3 = [[0]*3 for _ in range(3)]   # correct; [[0]*3]*3 aliases!

Generators & yield

# Lazy Fibonacci
def fib():
    a, b = 0, 1
    while True: yield a; a, b = b, a+b

from itertools import islice
print(list(islice(fib(), 10)))  # first 10
# yield from — delegate
def chain_all(*its):
    for it in its: yield from it

Generators hold state as a frozen call frame; resume on next().

Context Managers

# with-block pattern for setup/teardown
from contextlib import contextmanager

@contextmanager
def timer(label):
    import time
    t0 = time.perf_counter()
    try: yield
    finally: print(f"{label}: {(time.perf_counter()-t0)*1000:.2f}ms")

with timer("work"): do_thing()

# Multiple contexts in one with
with open("a") as f, open("b","w") as g: g.write(f.read())

Decorators

from functools import wraps
def retry(n=3):
    def deco(fn):
        @wraps(fn)
        def wrapped(*a, **kw):
            for i in range(n):
                try: return fn(*a, **kw)
                except Exception:
                    if i == n-1: raise
        return wrapped
    return deco

@retry(n=5)
def flaky(): ...

Unpacking, * / **

a, *mid, z = [1,2,3,4,5]      # a=1, mid=[2,3,4], z=5
head, *rest = ["a","b","c"]
def f(**kw): pass
f(**{"x":1, "y":2})            # forward kwargs
merged = {**a, **b}            # dict merge (Python 3.9+: a | b)
combined = [*list_a, *list_b]  # list concat

Slicing

s[::-1]        # reverse
s[::2]         # every second
s[:-1]         # drop last
a, b = s[:n//2], s[n//2:]
mat[::-1]      # reverse rows
[row[:3] for row in mat]  # first 3 cols

enumerate, zip, any, all

for i, x in enumerate(xs, start=1): ...
for a, b in zip(xs, ys): ...          # truncates to shorter
# Python 3.10+: zip(..., strict=True)   # raises on unequal length
all(x > 0 for x in xs)   # short-circuit
any(p(x)  for x in xs)

Walrus :=

while (chunk := f.read(8192)): process(chunk)
if (m := re.search(r"\d+", s)): print(m.group())

List Op Complexity

Python list operation complexity
MODULE 2

Python — stdlib Essentials

Batteries interviewers expect you to use.

collections

from collections import deque, defaultdict, Counter, OrderedDict, namedtuple

q = deque([1,2,3]); q.appendleft(0); q.popleft()    # O(1) both ends
graph = defaultdict(list)
graph[1].append(2)                                   # no KeyError

c = Counter("abracadabra")
c.most_common(2)       # [('a', 5), ('b', 2)]

Point = namedtuple("Point", "x y")
p = Point(1, 2); p.x, p.y
# dataclass is usually better for new code

heapq (min-heap)

import heapq
h = []
heapq.heappush(h, 3); heapq.heappush(h, 1)
heapq.heappop(h)        # 1

# heapify in place O(n)
arr = [5,2,8,1]; heapq.heapify(arr)
heapq.nlargest(3, arr); heapq.nsmallest(3, arr)

# max-heap trick: negate values
heapq.heappush(h, -value); -heapq.heappop(h)

bisect

import bisect
a = [1,2,4,4,5]
bisect.bisect_left(a, 4)   # 2  (first ≥ 4)
bisect.bisect_right(a, 4)  # 4  (first > 4)
bisect.insort(a, 3)        # keeps sorted; O(n) due to shift

insort is O(n) — shift cost dominates.

itertools

from itertools import combinations, permutations, product, chain, accumulate, groupby, islice, cycle, repeat

list(combinations([1,2,3], 2))      # (1,2),(1,3),(2,3)
list(permutations([1,2,3], 2))      # (1,2),(1,3),(2,1),(2,3),(3,1),(3,2)
list(product([0,1], repeat=3))      # all 3-bit tuples
list(chain([1,2],[3,4]))            # flatten
list(accumulate([1,2,3,4]))         # prefix sum [1,3,6,10]
# sorted+groupby to bucket
for k, g in groupby(sorted(items, key=key), key=key):
    print(k, list(g))

functools

from functools import lru_cache, cache, reduce, partial

@lru_cache(maxsize=None)
def fib(n): return n if n < 2 else fib(n-1)+fib(n-2)

@cache          # Python 3.9+: alias for lru_cache(maxsize=None)
def f(x): ...

reduce(lambda a,b: a*b, nums, 1)
p = partial(int, base=16); p("ff")  # 255

dataclasses & typing

from dataclasses import dataclass, field
from typing import Optional

@dataclass(frozen=True, slots=True)   # Python 3.10+ for slots
class User:
    id: int
    name: str
    tags: list[str] = field(default_factory=list)  # never default = []!

# typing
def get(x: int | None) -> str:        # Python 3.10+ union syntax
    return str(x) if x is not None else ""
MODULE 3

Python — Concurrency & Internals

GIL, asyncio, the pitfalls interviewers ask about.

The GIL

CPython's Global Interpreter Lock serializes bytecode execution — only one thread runs Python bytecode at a time. Threads DO help for I/O (the lock releases on I/O syscalls); they do NOT help CPU-bound work. For CPU-bound: multiprocessing, concurrent.futures.ProcessPoolExecutor, or C-extensions releasing the GIL (numpy). Python 3.13+ has optional free-threaded "no-GIL" build; not yet default.

asyncio

import asyncio, aiohttp

async def fetch(session, url):
    async with session.get(url) as r:
        return await r.text()

async def main(urls):
    async with aiohttp.ClientSession() as s:
        return await asyncio.gather(*(fetch(s, u) for u in urls))

asyncio.run(main(urls))

Common Pitfalls

  • Mutable default arg: def f(x=[]) — default list created once, shared across calls. Use x=None then assign inside.
  • Late binding in closures: [lambda: i for i in range(3)] all return 2. Fix: lambda i=i: i.
  • is vs ==: is = identity, == = equality. None, True, False use is; everything else ==.
  • Dict iteration order: insertion-ordered since 3.7 (CPython 3.6). OrderedDict still useful for move_to_end + equality semantics.
  • Shallow copy: a = b[:] or list(b) copies list but not nested objects. copy.deepcopy for full.
asyncio event loop — one tick
MODULE 4

Java — Collections

Which impl, which complexity.

Complexity Reference

Java Collection complexity

*LinkedList.remove given a node ref is O(1); by index is O(n).

HashMap Internals

Backing array of buckets; each bucket is a linked list. Java 8+: when a bucket grows past 8 entries AND table > 64, it converts to a red-black tree, improving worst-case from O(n) to O(log n). Default load factor 0.75 → resize (× 2) when size > capacity × 0.75.

ConcurrentHashMap

var m = new ConcurrentHashMap<String, AtomicInteger>();
m.computeIfAbsent("k", k -> new AtomicInteger()).incrementAndGet();
m.merge("k", 1, Integer::sum);

Lock-striped; iteration is weakly consistent (no ConcurrentModificationException). Prefer over Collections.synchronizedMap.

PriorityQueue

var pq = new PriorityQueue<int[]>((a,b) -> a[0] - b[0]);
pq.offer(new int[]{3, 'x'});
pq.peek(); pq.poll();
// max-heap: reverse comparator
var maxPq = new PriorityQueue<Integer>(Comparator.reverseOrder());
MODULE 5

Java — Streams & Concurrency

Declarative pipelines; thread pools; CompletableFuture.

Stream API

import static java.util.stream.Collectors.*;
var byDept = employees.stream()
    .filter(e -> e.salary() > 50_000)
    .collect(groupingBy(Employee::dept, counting()));

// reduce
int sum = nums.stream().mapToInt(Integer::intValue).sum();

// parallel — only for independent, CPU-bound, large work
int total = bigList.parallelStream().mapToInt(this::score).sum();

Optional

Optional<User> u = repo.find(id);
u.map(User::email).orElse("unknown");
// Don't do .get() without isPresent — misses the point.
// Don't use Optional for fields/params — only return types.

ExecutorService

var pool = Executors.newFixedThreadPool(8);
Future<Integer> f = pool.submit(() -> heavy());
int r = f.get();
pool.shutdown();
// Java 21 virtual threads
try (var es = Executors.newVirtualThreadPerTaskExecutor()) {
    es.submit(task);
}

CompletableFuture

CompletableFuture.supplyAsync(this::loadUser)
    .thenApply(User::email)
    .thenCompose(this::sendWelcome)
    .exceptionally(ex -> { log(ex); return null; });

var all = CompletableFuture.allOf(f1, f2, f3);
var any = CompletableFuture.anyOf(f1, f2);

Locks & Concurrency Utilities

  • synchronized + Object.wait/notify — low-level, avoid for new code.
  • ReentrantLock, ReadWriteLock — fairness options, tryLock with timeout.
  • Semaphore, CountDownLatch, CyclicBarrier, Phaser.
  • AtomicInteger, LongAdder — lock-free counters.
  • StampedLock — optimistic read + write locks.
MODULE 6

Java — Interview Patterns

Comparators, resources, records, switch.

Comparator idioms

Comparator<Person> byAgeThenName =
    Comparator.comparingInt(Person::age).thenComparing(Person::name);

// reverse
list.sort(Comparator.reverseOrder());
list.sort(byAgeThenName.reversed());

// nulls last
Comparator.nullsLast(Comparator.naturalOrder());

try-with-resources

try (var in = Files.newBufferedReader(p);
     var out = Files.newBufferedWriter(dst)) {
    out.write(in.readLine());
}  // closed in reverse order, exceptions suppressed-attached

Records (Java 16+)

public record Point(int x, int y) {
    public Point { if (x < 0 || y < 0) throw new IllegalArgumentException(); }  // compact ctor
    public int sum() { return x + y; }
}
// getters, equals, hashCode, toString auto-generated; final + immutable

Pattern Matching in switch (Java 21)

String describe(Object o) {
    return switch (o) {
        case Integer i when i < 0 -> "neg int";
        case Integer i -> "int " + i;
        case String s -> "string len=" + s.length();
        case null -> "null";
        default -> "other";
    };
}
MODULE 7

C++ — STL & Modern

Containers, algorithms, move semantics, smart pointers.

Container Complexity

C++ STL container complexity

* With iterator in hand.

<algorithm>

#include <algorithm>
#include <numeric>
std::sort(v.begin(), v.end());
std::sort(v.begin(), v.end(), std::greater<>{});
auto it = std::lower_bound(v.begin(), v.end(), x);
std::accumulate(v.begin(), v.end(), 0LL);
int cnt = std::count_if(v.begin(), v.end(), [](int x){ return x>0; });
std::unique(v.begin(), v.end());   // requires sorted first; returns new end
v.erase(std::unique(v.begin(), v.end()), v.end());  // erase-remove idiom

Move Semantics & RAII

std::vector<std::string> make() {
    std::vector<std::string> v;
    v.push_back(std::string(1000, 'x'));  // move from temporary
    return v;  // NRVO / move, not copy
}

class Buf {
    std::vector<char> data;
public:
    Buf(Buf&& o) noexcept : data(std::move(o.data)) {}   // move ctor
    Buf& operator=(Buf&& o) noexcept { data = std::move(o.data); return *this; }
};

RAII: resource lifetime = object lifetime. Acquire in ctor, release in dtor. No manual new/delete in modern C++.

Smart Pointers

auto p = std::make_unique<Widget>(arg);   // sole owner
auto s = std::make_shared<Widget>(arg);   // shared owner, atomic refcount
std::weak_ptr<Widget> w = s;              // non-owning observer
MODULE 8

Go — For Interviews

Slices, maps, goroutines, channels, context.

Slices (len vs cap)

s := make([]int, 0, 10)   // len=0 cap=10
s = append(s, 1, 2, 3)    // len=3 cap=10, no realloc
fmt.Println(len(s), cap(s))

// slicing shares backing array
a := []int{1,2,3,4,5}
b := a[1:3]               // b shares storage
b[0] = 99                 // mutates a[1]!
// defensive copy
c := make([]int, len(b)); copy(c, b)

Maps

m := map[string]int{}
m["a"] = 1
v, ok := m["a"]           // ok=false if missing
delete(m, "a")
// iteration order is randomized by design

Goroutines & Channels

ch := make(chan int, 3)   // buffered
go func() {
    for i := 0; i < 3; i++ { ch <- i }
    close(ch)
}()
for v := range ch { fmt.Println(v) }

// select — first ready wins
select {
case v := <-ch:        handle(v)
case <-time.After(1*time.Second): timeout()
case <-ctx.Done():     return ctx.Err()
}

sync

var (
    mu sync.Mutex
    wg sync.WaitGroup
)
for _, url := range urls {
    wg.Add(1)
    go func(u string) {
        defer wg.Done()
        r, err := http.Get(u)
        mu.Lock(); results[u] = r; mu.Unlock()
    }(url)
}
wg.Wait()

context.Context

ctx, cancel := context.WithTimeout(parent, 2*time.Second)
defer cancel()
req, _ := http.NewRequestWithContext(ctx, "GET", url, nil)

Every blocking or RPC call takes a context.Context as its first arg. Cancellation propagates automatically.

Go runtime scheduler — G/M/P model

Error handling

if err := doX(); err != nil {
    return fmt.Errorf("do X: %w", err)   // wrap
}
var nf *NotFound
if errors.As(err, &nf) { /* custom type */ }
if errors.Is(err, io.EOF) { /* sentinel */ }
MODULE 9

TypeScript — Essentials

Types, generics, narrowing, strict mode.

Types vs Interfaces

type User = { id: number; name: string };
interface User2 { id: number; name: string }

// interface: declaration merging, extends via `extends`
// type:      unions, intersections, mapped, conditional
type Role = "admin" | "user" | "guest";
type AdminUser = User & { role: "admin" };

Generics

function first<T>(xs: T[]): T | undefined { return xs[0]; }

// constraints
function getKey<T, K extends keyof T>(obj: T, k: K): T[K] { return obj[k]; }

class Cache<T> {
    private map = new Map<string, T>();
    get(k: string): T | undefined { return this.map.get(k); }
}

Utility Types

type U  = Partial<User>;           // all optional
type U2 = Required<U>;
type U3 = Readonly<User>;
type P  = Pick<User, "id" | "name">;
type O  = Omit<User, "id">;
type R  = Record<Role, User[]>;    // map Role to User[]
type T  = ReturnType<typeof fn>;
type A  = Awaited<Promise<string>>;   // string

Discriminated Unions + Narrowing

type Shape =
    | { kind: "circle"; r: number }
    | { kind: "square"; side: number };

function area(s: Shape): number {
    switch (s.kind) {
        case "circle": return Math.PI * s.r ** 2;   // s narrowed
        case "square": return s.side ** 2;
    }
}

// Type guard fn
function isErr(x: unknown): x is Error {
    return x instanceof Error;
}

Strict Mode

Enable "strict": true in tsconfig. This turns on noImplicitAny, strictNullChecks, strictFunctionTypes, and more. any defeats the type system — prefer unknown + narrowing.

TypeScript narrowing paths
MODULE 10

Language-agnostic Interview Habits

How to write good code under time pressure.

Naming

  • Full words > abbreviations. customer_count > custCnt.
  • Boolean names start with is, has, can.
  • Collections in plural. Loop variables short: i, j, k for indices, single noun for items.
  • Match the domain, not the implementation. orders > order_list.

Early Return & Guard Clauses

# prefer this
def process(req):
    if not req.valid: return Err(422)
    if not req.user.active: return Err(403)
    # happy path, flat

Deep nesting hides the happy path. Flatten with early return.

Error Handling

  • Handle errors at the right boundary. Don't catch-and-swallow deep inside.
  • Attach context when wrapping: fmt.Errorf("load user %d: %w", id, err).
  • Distinguish programmer errors (panics, assertions) from user-visible errors.

Test Skeleton

In interview, leave an empty test block — shows you think test-first. e.g. "# test: empty list returns []; list of dups returns 1; mixed returns sorted unique".

Communication

  • Talk while you think: state your current approach before coding.
  • Confirm constraints before optimizing ("is n ≤ 10⁵? Then O(n log n) is fine").
  • When stuck: enumerate what you know, what you don't. Ask for a hint if truly stuck — that's fine.
  • Before declaring done: trace through one case; state time & space.