Java Syntax for DSA
The Java arrays, strings and collections syntax you type in every coding round, with compiled snippets and the Big-O of each operation.
In an interview you want to spend your thinking on the algorithm, not on whether it is length or size(), or how to write a max-heap. This sheet collects the Java syntax that shows up in almost every DSA problem, grouped by data structure, with the time complexity of each operation.
Skim it before a coding round. Every snippet compiles and can be opened in the site's editor to run and tweak; tick an item once you can write it from memory, and read the bullets — they are the gotchas interviewers notice.
Arrays
Fixed-size, zero-indexed, fastest random access.
1Declare & initialise (1D / 2D)
Sizes are fixed at creation and elements start at their default value.
arr.lengthis a field (no parentheses); Strings uselength(), lists usesize().- Defaults: 0 for numbers, false for boolean, '\u0000' for char, null for objects.
int[] a = new int[5]; // [0, 0, 0, 0, 0] int[] b = {3, 1, 2}; // array literal int[][] grid = new int[3][4]; // 3 rows x 4 cols, all 0 int[][] g2 = {{1, 2}, {3, 4}}; String[] names = new String[2]; // [null, null] boolean[] seen = new boolean[4]; // all false System.out.println(b.length + " " + grid.length + " " + grid[0].length); // 3 3 4
2Iterate: index loop & for-each
Use the index loop when you need
i; for-each when you only need values.- Assigning to
xinside for-each does NOT change the array.
int[] nums = {4, 5, 6}; for (int i = 0; i < nums.length; i++) System.out.print(nums[i] + " "); // 4 5 6 for (int x : nums) System.out.print(x + " "); // 4 5 6 int[][] grid = {{1, 2}, {3, 4}}; for (int[] row : grid) for (int v : row) System.out.print(v + " "); // 1 2 3 4
- Assigning to
3Arrays.fill
Sets every slot to one value, e.g. infinity for distances or -1 for memo tables.
Arrays.fillonly works on one dimension; loop over the rows of a 2D array.
int[] dist = new int[4]; Arrays.fill(dist, Integer.MAX_VALUE); int[][] memo = new int[3][3]; for (int[] row : memo) Arrays.fill(row, -1); // fill row by row System.out.println(Arrays.toString(memo[0])); // [-1, -1, -1]
4Arrays.sort
Sorts in place in ascending order; an optional range sorts
[from, to).- Descending order needs a boxed
Integer[];int[]takes no comparator. - For
int[]descending: sort ascending, then read it backwards.
int[] a = {5, 2, 9, 1}; Arrays.sort(a); // [1, 2, 5, 9] int[] b = {9, 7, 3, 1}; Arrays.sort(b, 1, 3); // [9, 3, 7, 1] (only indices 1..2) Integer[] boxed = {5, 2, 9, 1}; Arrays.sort(boxed, Collections.reverseOrder()); // [9, 5, 2, 1]
- Descending order needs a boxed
5Sort int[][] by a column
Pass a comparator to order rows, e.g. intervals by start time.
- Break ties with a second key:
x[0] != y[0] ? Integer.compare(x[0], y[0]) : Integer.compare(x[1], y[1]).
Practice: Merge Intervals →int[][] intervals = {{5, 6}, {1, 3}, {2, 4}}; Arrays.sort(intervals, (x, y) -> Integer.compare(x[0], y[0])); // by start // same: Arrays.sort(intervals, Comparator.comparingInt(x -> x[0])); System.out.println(Arrays.deepToString(intervals)); // [[1, 3], [2, 4], [5, 6]]
- Break ties with a second key:
6Arrays.toString / deepToString
Printing an array directly shows a type@hash, not its contents.
- Compare contents with
Arrays.equals(a, b);a == bcompares references.
int[] a = {1, 2, 3}; System.out.println(a); // e.g. [I@6d06d69c (not the contents!) System.out.println(Arrays.toString(a)); // [1, 2, 3] int[][] g = {{1, 2}, {3, 4}}; System.out.println(Arrays.deepToString(g)); // [[1, 2], [3, 4]]
- Compare contents with
7Copying: copyOfRange, copyOf, clone
Create a real copy; plain assignment only copies the reference.
clone()on a 2D array is shallow: the rows are still shared.
int[] a = {1, 2, 3, 4, 5}; int[] part = Arrays.copyOfRange(a, 1, 4); // [2, 3, 4] (end exclusive) int[] bigger = Arrays.copyOf(a, 7); // [1, 2, 3, 4, 5, 0, 0] int[] copy = a.clone(); // independent copy int[] alias = a; // SAME array, not a copy alias[0] = 99; // a[0] is now 99, copy[0] is still 1
| Operation | Code | Time |
|---|---|---|
| Access / update | a[i], a[i] = x | O(1) |
| Linear search | loop | O(n) |
| Binary search (sorted) | Arrays.binarySearch(a, x) | O(log n) |
| Sort primitives | Arrays.sort(int[]) | O(n log n) |
| Sort objects (stable) | Arrays.sort(T[], cmp) | O(n log n) |
| Fill | Arrays.fill(a, v) | O(n) |
| Copy range | Arrays.copyOfRange(a, i, j) | O(j - i) |
Strings & StringBuilder
Strings are immutable; build new text with StringBuilder.
8length, charAt, toCharArray
Read characters by index, or copy to a
char[]when you need to sort or edit.String s = "hello"; int n = s.length(); // 5 (a method, unlike array.length) char c = s.charAt(1); // 'e' char[] cs = s.toCharArray(); // mutable copy Arrays.sort(cs); String sorted = new String(cs); // "ehllo"
9substring & indexOf
Extract part of a string; the end index is exclusive.
substringcopies characters, so it costs O(length) — not free inside a loop.
String s = "interview"; String a = s.substring(0, 5); // "inter" (begin inclusive, end exclusive) String b = s.substring(5); // "view" (to the end) int at = s.indexOf("view"); // 5 (-1 if absent) boolean has = s.contains("tv"); // false
10equals vs ==, compareTo
Compare string contents with
equals;==only checks if both are the same object.compareToreturns negative / 0 / positive — never rely on it being exactly -1 or 1.
String a = "abc", b = new String("abc"); System.out.println(a == b); // false (different objects) System.out.println(a.equals(b)); // true (same characters) System.out.println(a.equalsIgnoreCase("ABC")); // true System.out.println("apple".compareTo("banana") < 0); // true (lexicographic)
11split, join, valueOf, parseInt
Convert between strings, pieces and numbers.
splittakes a regex: split on a dot with"\\.", on a pipe with"\\|".
String[] parts = "a,b,,c".split(","); // [a, b, , c] String[] words = " hi there ".trim().split("\\s+"); // [hi, there] String joined = String.join("-", words); // "hi-there" String num = String.valueOf(42); // "42" int back = Integer.parseInt("42"); // 42
12char arithmetic
Characters are numbers, so
c - 'a'maps 'a'..'z' to 0..25.- A 26-slot array beats a HashMap for lowercase-letter counts.
Pattern: Hashing →String s = "leetcode"; int[] freq = new int[26]; for (char c : s.toCharArray()) freq[c - 'a']++; System.out.println(freq['e' - 'a']); // 3 char next = (char) ('a' + 2); // 'c' (cast back to char) int digit = '7' - '0'; // 7
13Character helpers
Static checks and conversions on single characters.
- Handy for 'valid palindrome' style problems that skip punctuation.
char c = 'B'; boolean d = Character.isDigit('5'); // true boolean l = Character.isLetter(c); // true boolean ld = Character.isLetterOrDigit('_'); // false boolean up = Character.isUpperCase(c); // true char lower = Character.toLowerCase(c); // 'b'
14StringBuilder
A mutable character buffer; most methods return the builder so calls can chain.
- Use
sb.length()andsb.charAt(i)just like a String. - Backtracking: undo an append with
sb.deleteCharAt(sb.length() - 1).
StringBuilder sb = new StringBuilder("abc"); sb.append('d').append(12); // "abcd12" sb.insert(0, "x"); // "xabcd12" sb.deleteCharAt(sb.length() - 1); // "xabcd1" sb.setCharAt(0, 'X'); // "Xabcd1" sb.reverse(); // "1dcbaX" String out = sb.toString();
- Use
15Why += in a loop is O(n^2)
Each
+=copies the whole string into a new one; StringBuilder appends in place.String slow = ""; for (int i = 0; i < 1000; i++) slow += i; // copies every time: O(n^2) StringBuilder sb = new StringBuilder(); for (int i = 0; i < 1000; i++) sb.append(i); // amortised O(1) per append String fast = sb.toString(); System.out.println(slow.equals(fast)); // true, same result
| Operation | Code | Time |
|---|---|---|
| Read a char | s.charAt(i) | O(1) |
| Substring | s.substring(i, j) | O(j - i) |
| Compare | s.equals(t), s.compareTo(t) | O(n) |
| Search | s.indexOf(t), s.contains(t) | O(n·m) worst |
| Append | sb.append(x) | O(1) amortised |
| Insert / delete at front | sb.insert(0, x), sb.deleteCharAt(0) | O(n) |
| Reverse | sb.reverse() | O(n) |
HashMap
Key -> value lookups in O(1) average time.
16put, get, getOrDefault, containsKey, remove
The core map API;
getreturns null for missing keys.int x = map.get(missingKey)throws NullPointerException (unboxing null).- Compare
Integervalues from two maps with.equals, not==(caching only covers -128..127).
Map<String, Integer> age = new HashMap<>(); age.put("ana", 30); age.put("ana", 31); // overwrites -> 31 int a = age.get("ana"); // 31 Integer missing = age.get("bob"); // null int safe = age.getOrDefault("bob", 0); // 0 boolean has = age.containsKey("ana"); // true age.remove("ana"); System.out.println(age.size()); // 0
17Counting with merge
merge(k, 1, Integer::sum)inserts 1 or adds 1 to the existing count.- Decrement and drop at zero:
if (count.merge(c, -1, Integer::sum) == 0) count.remove(c);
Practice: Two Sum →String s = "banana"; Map<Character, Integer> count = new HashMap<>(); for (char c : s.toCharArray()) count.merge(c, 1, Integer::sum); System.out.println(count); // {a=3, b=1, n=2} // same thing: count.put(c, count.getOrDefault(c, 0) + 1);
- Decrement and drop at zero:
18Grouping with computeIfAbsent
Create the bucket list on first use, then add to it, in one line.
- Also the standard way to build an adjacency list:
graph.computeIfAbsent(u, x -> new ArrayList<>()).add(v).
Practice: Group Anagrams →String[] words = {"eat", "tea", "tan", "ate"}; Map<String, List<String>> groups = new HashMap<>(); for (String w : words) { char[] k = w.toCharArray(); Arrays.sort(k); groups.computeIfAbsent(new String(k), x -> new ArrayList<>()).add(w); } System.out.println(groups.get("aet")); // [eat, tea, ate]
- Also the standard way to build an adjacency list:
19Iterate entrySet / keySet / values
Use
entrySetwhen you need both key and value.- HashMap order is unspecified: use LinkedHashMap for insertion order, TreeMap for sorted keys.
- Calling
m.removeinside a for-each loop throws ConcurrentModificationException.
Map<String, Integer> m = new HashMap<>(Map.of("x", 1, "y", 2)); for (Map.Entry<String, Integer> e : m.entrySet()) System.out.println(e.getKey() + "=" + e.getValue()); for (String k : m.keySet()) System.out.print(k + " "); for (int v : m.values()) System.out.print(v + " "); m.entrySet().removeIf(e -> e.getValue() < 2); // safe removal while iterating
| Operation | Code | Time |
|---|---|---|
| Insert / update | map.put(k, v) | O(1) avg |
| Search by key | map.get(k), containsKey(k) | O(1) avg |
| Delete | map.remove(k) | O(1) avg |
| Count / group | merge, computeIfAbsent | O(1) avg |
| Search by value | map.containsValue(v) | O(n) |
| Iterate | entrySet / keySet / values | O(n) |
HashSet
Unique values with O(1) average membership checks.
20add, contains, remove, size
addreturns false when the value is already present.- Use
if (!set.add(x))to detect a duplicate in one call.
Practice: Contains Duplicate →Set<Integer> seen = new HashSet<>(); System.out.println(seen.add(5)); // true (newly added) System.out.println(seen.add(5)); // false (already present) boolean has = seen.contains(5); // true seen.remove(5); System.out.println(seen.size()); // 0
- Use
21Build from a collection & iterate
Copying a list into a set removes duplicates.
Set.of("a", "a")throws IllegalArgumentException on duplicates and is immutable.- Need a sorted or insertion-ordered set? Use TreeSet or LinkedHashSet.
List<String> raw = List.of("a", "b", "a"); Set<String> s = new HashSet<>(raw); System.out.println(s.size()); // 2 for (String x : s) System.out.print(x + " "); // order not guaranteed
22Set operations: retainAll / addAll / removeAll
Intersection, union and difference — copy first because these mutate the set.
Set<Integer> a = new HashSet<>(List.of(1, 2, 3)); Set<Integer> b = new HashSet<>(List.of(2, 3, 4)); Set<Integer> inter = new HashSet<>(a); inter.retainAll(b); // [2, 3] Set<Integer> union = new HashSet<>(a); union.addAll(b); // [1, 2, 3, 4] Set<Integer> diff = new HashSet<>(a); diff.removeAll(b); // [1] System.out.println(inter + " " + union + " " + diff);
| Operation | Code | Time |
|---|---|---|
| Insert | set.add(x) | O(1) avg |
| Membership | set.contains(x) | O(1) avg |
| Delete | set.remove(x) | O(1) avg |
| Intersection / union | retainAll / addAll | O(n + m) avg |
ArrayList & List
Growable array; the default list in Java.
23add, add(i, x), get, set, size, contains
Program to the
Listinterface and use ArrayList underneath.List<Integer> list = new ArrayList<>(); list.add(10); list.add(20); // [10, 20] list.add(0, 5); // [5, 10, 20] (shifts the rest right) int first = list.get(0); // 5 list.set(1, 99); // [5, 99, 20] System.out.println(list.size() + " " + list.contains(20)); // 3 true
24remove(index) vs remove(value)
With
List<Integer>,remove(1)removes index 1, not the value 1.remove(Object)deletes only the first match.
List<Integer> list = new ArrayList<>(List.of(10, 20, 30)); list.remove(1); // removes INDEX 1 -> [10, 30] list.remove(Integer.valueOf(10)); // removes VALUE 10 -> [30] System.out.println(list); // [30]
25Sorting a list
Sort in place ascending or with any comparator.
List<Integer> list = new ArrayList<>(List.of(3, 1, 2)); Collections.sort(list); // [1, 2, 3] list.sort(Collections.reverseOrder()); // [3, 2, 1] list.sort(null); // natural order again: [1, 2, 3] System.out.println(list); // [1, 2, 3]
26List.of is immutable
List.ofgives a fixed list; wrap it innew ArrayList<>(...)to modify.- Backtracking bug: adding
pathitself instead ofnew ArrayList<>(path)leaves you with empty lists. List.ofalso rejects null elements.
Pattern: Backtracking →List<Integer> fixed = List.of(1, 2, 3); // fixed.add(4); -> UnsupportedOperationException at runtime List<Integer> copy = new ArrayList<>(fixed); // mutable copy copy.add(4); // [1, 2, 3, 4] List<List<Integer>> res = new ArrayList<>(); res.add(new ArrayList<>(copy)); // snapshot, not a live reference
- Backtracking bug: adding
27Convert int[] <-> List<Integer>
Primitive arrays need boxing; a plain loop is always fine.
Arrays.asList(intArray)gives aList<int[]>of size 1 — not what you want.
int[] arr = {3, 1, 2}; List<Integer> list = Arrays.stream(arr).boxed().collect(Collectors.toList()); int[] back = list.stream().mapToInt(Integer::intValue).toArray(); List<Integer> viaLoop = new ArrayList<>(); for (int x : arr) viaLoop.add(x); System.out.println(list + " " + Arrays.toString(back)); // [3, 1, 2] [3, 1, 2]
| Operation | Code | Time |
|---|---|---|
| Read / write by index | get(i), set(i, x) | O(1) |
| Append | add(x) | O(1) amortised |
| Insert / remove in middle | add(i, x), remove(i) | O(n) |
| Remove last | remove(list.size() - 1) | O(1) |
| Search | contains(x), indexOf(x) | O(n) |
| Sort | Collections.sort / list.sort | O(n log n) |
PriorityQueue (Heap)
Always hands you the smallest (or largest) element fast.
28Min-heap (default)
With no comparator, the smallest element is at the head.
- Printing or iterating a heap does NOT give sorted order; only
poll()does. peek()/poll()return null on an empty heap.
PriorityQueue<Integer> minHeap = new PriorityQueue<>(); minHeap.offer(5); minHeap.offer(1); minHeap.offer(3); System.out.println(minHeap.peek()); // 1 (smallest, not removed) System.out.println(minHeap.poll()); // 1 (removed) System.out.println(minHeap.size()); // 2 System.out.println(minHeap.isEmpty()); // false
- Printing or iterating a heap does NOT give sorted order; only
29Max-heap
Reverse the ordering with
Collections.reverseOrder()or a comparator.(a, b) -> b - aalso works but can overflow for large/negative values; preferInteger.compare(b, a).
PriorityQueue<Integer> maxHeap = new PriorityQueue<>(Collections.reverseOrder()); // equivalent: new PriorityQueue<>((a, b) -> Integer.compare(b, a)); maxHeap.addAll(List.of(5, 1, 3)); System.out.println(maxHeap.poll()); // 5 System.out.println(maxHeap.poll()); // 3
30Heap of int[] pairs
Order arrays by one of their fields, e.g. {node, dist} for Dijkstra.
Pattern: Shortest path →// MIN-heap of {node, dist}: smallest dist comes out first PriorityQueue<int[]> pq = new PriorityQueue<>((x, y) -> Integer.compare(x[1], y[1])); pq.offer(new int[]{0, 7}); pq.offer(new int[]{1, 2}); int[] top = pq.poll(); System.out.println(top[0] + " " + top[1]); // 1 2
31Heap of Map.Entry ordered by value
Put map entries in a heap to pull keys by their counts.
- Swap
xandyto flip min <-> max; label your heap in a comment so you don't mix them up.
Practice: Top K Frequent Elements →Map<String, Integer> freq = Map.of("a", 5, "b", 2, "c", 9); // MAX-heap by value: highest count comes out first PriorityQueue<Map.Entry<String, Integer>> maxByCount = new PriorityQueue<>((x, y) -> Integer.compare(y.getValue(), x.getValue())); maxByCount.addAll(freq.entrySet()); System.out.println(maxByCount.poll().getKey()); // c // MIN-heap by value: (x, y) -> Integer.compare(x.getValue(), y.getValue())
- Swap
32Top-k with a size-k min-heap
Keep only k items; the head is then the k-th largest.
- O(n log k) time and O(k) space, better than sorting when k is small.
Pattern: Heap / Top-K →int[] nums = {4, 1, 7, 3, 9, 2}; int k = 3; PriorityQueue<Integer> heap = new PriorityQueue<>(); // min-heap for (int x : nums) { heap.offer(x); if (heap.size() > k) heap.poll(); // evict the smallest } System.out.println(heap.peek()); // 4 (3rd largest)
| Operation | Code | Time |
|---|---|---|
| Insert | pq.offer(x) | O(log n) |
| Remove min/max | pq.poll() | O(log n) |
| Read min/max | pq.peek() | O(1) |
| Size / empty | pq.size(), pq.isEmpty() | O(1) |
| Remove arbitrary / contains | pq.remove(x), pq.contains(x) | O(n) |
| Build from collection | new PriorityQueue<>(list) | O(n) |
Queue & Deque
FIFO queues for BFS; double-ended queues for stacks and windows.
33Queue: offer, poll, peek
First in, first out — the backbone of BFS.
poll()/peek()return null when empty;remove()/element()throw instead.- ArrayDeque does not allow null elements; LinkedList does.
Pattern: Graph BFS →Queue<Integer> q = new ArrayDeque<>(); // or new LinkedList<>() q.offer(1); q.offer(2); q.offer(3); System.out.println(q.peek()); // 1 (front, not removed) System.out.println(q.poll()); // 1 (removed) System.out.println(q.size()); // 2
34Deque as a stack
push,popandpeekall work on the front of an ArrayDeque.- Preferred over
java.util.Stack: not synchronized, faster, and the Java docs recommend it.
Deque<Integer> stack = new ArrayDeque<>(); stack.push(1); stack.push(2); stack.push(3); System.out.println(stack.peek()); // 3 (top) System.out.println(stack.pop()); // 3 System.out.println(stack); // [2, 1] (top first)
- Preferred over
35Both ends: offerFirst / offerLast / pollFirst / pollLast
Add, read or remove at either end in O(1).
Deque<Integer> dq = new ArrayDeque<>(); dq.offerLast(2); dq.offerLast(3); // [2, 3] dq.offerFirst(1); // [1, 2, 3] System.out.println(dq.peekFirst() + " " + dq.peekLast()); // 1 3 dq.pollFirst(); // [2, 3] dq.pollLast(); // [2] System.out.println(dq); // [2]
36Sliding window maximum (monotonic deque)
Keep indices whose values decrease; the front is the window max.
- Each index is added and removed at most once, so the whole pass is O(n).
Practice: Sliding Window Maximum →int[] nums = {1, 3, -1, -3, 5, 3, 6, 7}; int k = 3; Deque<Integer> dq = new ArrayDeque<>(); // indices, values decreasing List<Integer> out = new ArrayList<>(); for (int i = 0; i < nums.length; i++) { if (!dq.isEmpty() && dq.peekFirst() <= i - k) dq.pollFirst(); // out of window while (!dq.isEmpty() && nums[dq.peekLast()] <= nums[i]) dq.pollLast(); dq.offerLast(i); if (i >= k - 1) out.add(nums[dq.peekFirst()]); } System.out.println(out); // [3, 3, 5, 5, 6, 7]
| Operation | Code | Time |
|---|---|---|
| Enqueue / dequeue | offer(x), poll() | O(1) |
| Front of queue | peek() | O(1) |
| Push / pop (stack) | push(x), pop(), peek() | O(1) |
| Either end | offerFirst/Last, pollFirst/Last, peekFirst/Last | O(1) |
| Search | contains(x) | O(n) |
Stack
Last in, first out — know the legacy class, use Deque.
37Legacy java.util.Stack
Still common in older code and interview answers; the API is push/pop/peek/isEmpty.
- Stack extends Vector: every call is synchronized and it exposes index methods that break LIFO.
pop()/peek()on an empty Stack throw EmptyStackException.
Stack<Integer> st = new Stack<>(); st.push(1); st.push(2); System.out.println(st.peek()); // 2 System.out.println(st.pop()); // 2 System.out.println(st.isEmpty()); // false System.out.println(st); // [1] (printed bottom first)
38Recommended: Deque<> stack = new ArrayDeque<>()
Same push/pop/peek calls, faster and cleaner — use it in interviews.
- On ArrayDeque,
pop()on empty throws NoSuchElementException;poll()/peek()return null.
Practice: Valid Parentheses →String s = "({[]})"; Deque<Character> st = new ArrayDeque<>(); boolean ok = true; for (char c : s.toCharArray()) { if (c == '(' || c == '[' || c == '{') st.push(c); else if (st.isEmpty() || "([{".indexOf(st.pop()) != ")]}".indexOf(c)) { ok = false; break; } } ok = ok && st.isEmpty(); System.out.println(ok); // true
- On ArrayDeque,
LinkedList & custom nodes
Hand-rolled nodes for list/tree problems, plus the built-in LinkedList.
39ListNode + dummy head
A dummy node before the head removes special cases when building or deleting.
- Return
dummy.next, neverdummy. - Always null-check
p.nextbefore readingp.next.next.
Pattern: Linked list →class ListNode { int val; ListNode next; ListNode(int val) { this.val = val; } } ListNode dummy = new ListNode(0), tail = dummy; for (int v : new int[]{1, 2, 3}) { tail.next = new ListNode(v); tail = tail.next; } ListNode head = dummy.next; // 1 -> 2 -> 3 for (ListNode p = head; p != null; p = p.next) System.out.print(p.val + " "); // 1 2 3
- Return
40TreeNode definition
The standard binary-tree node used by almost every tree problem.
- In a full program declare it as a
static class(LeetCode already provides it).
Pattern: Tree DFS →class TreeNode { int val; TreeNode left, right; TreeNode(int val) { this.val = val; } } TreeNode root = new TreeNode(2); root.left = new TreeNode(1); root.right = new TreeNode(3); System.out.println(root.left.val + " " + root.val + " " + root.right.val); // 1 2 3
- In a full program declare it as a
41java.util.LinkedList API
A doubly linked list that is also a List and a Deque.
get(i)walks the list: O(n). Prefer ArrayList for index access, ArrayDeque for queues.
LinkedList<Integer> ll = new LinkedList<>(); ll.add(2); ll.addFirst(1); ll.addLast(3); // [1, 2, 3] System.out.println(ll.getFirst() + " " + ll.getLast()); // 1 3 ll.removeFirst(); // [2, 3] ll.removeLast(); // [2] System.out.println(ll); // [2]
| Operation | Code | Time |
|---|---|---|
| Add / remove at ends | addFirst, addLast, removeFirst, removeLast | O(1) |
| Read ends | getFirst(), getLast() | O(1) |
| Access by index | get(i) | O(n) |
| Search | contains(x) | O(n) |
TreeMap & TreeSet
Sorted keys plus floor/ceiling lookups in O(log n).
42TreeMap: ordered keys, floorKey, ceilingKey
Keys stay sorted; floor/ceiling find the nearest key below or above a value.
lowerKey/higherKeyare the strict (< / >) versions.- Results can be null — use
Integer, notint, to hold them.
TreeMap<Integer, String> tm = new TreeMap<>(); tm.put(30, "c"); tm.put(10, "a"); tm.put(20, "b"); System.out.println(tm); // {10=a, 20=b, 30=c} System.out.println(tm.firstKey() + " " + tm.lastKey()); // 10 30 System.out.println(tm.floorKey(25)); // 20 (largest key <= 25) System.out.println(tm.ceilingKey(25)); // 30 (smallest key >= 25) System.out.println(tm.floorKey(5)); // null (none)
43headMap, tailMap, pollFirstEntry
Range views and removing the smallest entry.
- Views are live: changes to them write through to the map.
TreeMap<Integer, String> tm = new TreeMap<>(Map.of(10, "a", 20, "b", 30, "c")); System.out.println(tm.headMap(20)); // {10=a} (keys < 20) System.out.println(tm.headMap(20, true)); // {10=a, 20=b} (keys <= 20) System.out.println(tm.tailMap(20)); // {20=b, 30=c} (keys >= 20) System.out.println(tm.pollFirstEntry()); // 10=a (and removes it) System.out.println(tm); // {20=b, 30=c}
44TreeSet
A sorted set with the same nearest-value queries.
- Use a TreeMap<value, count> when you need a sorted multiset (duplicates).
TreeSet<Integer> ts = new TreeSet<>(List.of(5, 1, 9, 3)); System.out.println(ts); // [1, 3, 5, 9] System.out.println(ts.floor(4)); // 3 System.out.println(ts.ceiling(4)); // 5 System.out.println(ts.higher(5)); // 9 (strictly greater) System.out.println(ts.pollFirst()); // 1 (removes the smallest) System.out.println(ts.last()); // 9
| Operation | Code | Time |
|---|---|---|
| Insert / delete / lookup | put, remove, get, containsKey | O(log n) |
| Nearest key | floorKey, ceilingKey, lowerKey, higherKey | O(log n) |
| Smallest / largest | firstKey, lastKey, first, last | O(log n) |
| Remove smallest | pollFirstEntry, pollFirst | O(log n) |
| Range view | headMap, tailMap, subMap | O(log n) to create |
| Iterate in order | for-each | O(n) |
Sorting & comparators
Describe the order once, reuse it for sort, heaps and TreeMaps.
45comparingInt, thenComparing, reversed
Build multi-key orderings declaratively, e.g. on a record.
.reversed()flips the WHOLE chain built so far.- With lambdas, chaining may need a typed parameter:
Comparator.comparingInt((int[] a) -> a[0]).reversed().
record Person(String name, int age) {} List<Person> ps = new ArrayList<>(List.of( new Person("Cy", 30), new Person("Al", 25), new Person("Bo", 30))); ps.sort(Comparator.comparingInt(Person::age).thenComparing(Person::name)); System.out.println(ps.get(0).name() + ps.get(1).name() + ps.get(2).name()); // AlBoCy ps.sort(Comparator.comparingInt(Person::age).reversed()); System.out.println(ps.get(0).age()); // 30
46Lambda comparators
Return negative if
agoes first, positive ifbgoes first, 0 if tied.- Object sorts are stable (TimSort): equal elements keep their original order.
String[] words = {"pear", "fig", "banana", "kiwi"}; // shorter first; ties alphabetical Arrays.sort(words, (a, b) -> a.length() != b.length() ? Integer.compare(a.length(), b.length()) : a.compareTo(b)); System.out.println(Arrays.toString(words)); // [fig, kiwi, pear, banana]
47Integer.compare vs subtraction
Subtracting ints can overflow and invert the order;
Integer.comparenever does.- Use
Long.comparefor longs andDouble.comparefor doubles.
int a = Integer.MAX_VALUE, b = -1; System.out.println(a - b); // -2147483648: overflow claims a < b! System.out.println(Integer.compare(a, b)); // 1: correct, a > b Comparator<Integer> asc = Integer::compare; Comparator<Integer> desc = (x, y) -> Integer.compare(y, x);
- Use
48Natural, reverse and case-insensitive order
Ready-made comparators for common orderings.
Collections.reverseOrder()andComparator.reverseOrder()give the same ordering — both need the().
List<String> names = new ArrayList<>(List.of("bob", "Al", "cy")); names.sort(Comparator.naturalOrder()); // [Al, bob, cy] (uppercase sorts first) names.sort(Comparator.reverseOrder()); // [cy, bob, Al] names.sort(String.CASE_INSENSITIVE_ORDER); // [Al, bob, cy] System.out.println(names); // [Al, bob, cy]
Handy utilities
Math, overflow guards, parsing and bit tricks you reach for constantly.
49Math helpers, limits & long overflow
Know the int limits and widen to long before multiplying big values.
Math.powreturns a double; avoid it for exact integer powers.Math.abs(Integer.MIN_VALUE)is still negative.- Use
Math.floorMod(x, m)to get a non-negative remainder for negative x.
int a = Math.max(3, 7), b = Math.min(3, 7), c = Math.abs(-4); // 7 3 4 int best = Integer.MIN_VALUE; // -2147483648: start value for a max search int lowest = Integer.MAX_VALUE; // 2147483647: start value for a min search long ok = (long) 100000 * 100000; // 10000000000 (cast BEFORE multiplying) int bad = 100000 * 100000; // 1410065408 (silently overflowed) System.out.println(ok + " " + bad);
50Safe midpoint
lo + (hi - lo) / 2or(lo + hi) >>> 1avoid overflow in binary search.Pattern: Binary search →int lo = 2_000_000_000, hi = 2_100_000_000; int wrong = (lo + hi) / 2; // -97483648 (lo + hi overflowed) int mid1 = lo + (hi - lo) / 2; // 2050000000 int mid2 = (lo + hi) >>> 1; // 2050000000 (unsigned shift) System.out.println(wrong + " " + mid1 + " " + mid2);
51Parsing & number <-> string
Convert text to numbers and back, including other bases.
parseIntthrows NumberFormatException on bad input such as "" or "12a".
int n = Integer.parseInt("-123"); // -123 long big = Long.parseLong("9000000000"); // too big for int String s = Integer.toString(255); // "255" String bin = Integer.toBinaryString(5); // "101" int fromBin = Integer.parseInt("101", 2); // 5 char d = (char) ('0' + 7); // '7'
52Collections.swap / reverse / frequency / max
Static helpers that work on any List.
List<Integer> list = new ArrayList<>(List.of(1, 2, 3, 2)); Collections.swap(list, 0, 3); // [2, 2, 3, 1] Collections.reverse(list); // [1, 3, 2, 2] int twos = Collections.frequency(list, 2); // 2 int mx = Collections.max(list), mn = Collections.min(list); // 3 1 System.out.println(list + " " + twos + " " + mx + " " + mn); // [1, 3, 2, 2] 2 3 1
53Arrays.asList caveat
Returns a fixed-size view backed by the array, not a normal ArrayList.
String[] arr = {"b", "a"}; List<String> view = Arrays.asList(arr); // fixed-size, backed by arr view.set(0, "z"); // arr[0] is now "z" too // view.add("c"); -> UnsupportedOperationException List<String> real = new ArrayList<>(Arrays.asList(arr)); // fully mutable int[] prim = {1, 2}; System.out.println(Arrays.asList(prim).size()); // 1 (a List<int[]>!)
54Bit tricks
Constant-time checks on the binary form of an int.
x ^ x == 0andx ^ 0 == x: XOR all numbers to find the one without a pair.- Use
>>>for an unsigned right shift of negative numbers.
int n = 12; // binary 1100 boolean odd = (n & 1) == 1; // false int dropLow = n & (n - 1); // 8 (1000): clears the lowest set bit boolean pow2 = n > 0 && (n & (n - 1)) == 0; // false int ones = Integer.bitCount(n); // 2 int lowBit = n & -n; // 4: isolates the lowest set bit int half = n >> 1, dbl = n << 1; // 6, 24
Quick tips
- Declare with interfaces (
List,Map,Set,Deque,Queue) and pick the implementation on the right-hand side. - Reach for
ArrayDequefor both stacks and queues; mention why if you skipStack. - Use
Integer.compare(nevera - b) in comparators, and cast tolongbefore multiplying large ints. - Compare objects (Strings, Integers) with
.equals, never==. - Say the complexity of each collection call out loud — interviewers listen for O(1) avg vs O(log n) vs O(n).
Inspired by a public Java Syntax Notes revision handout; rewritten, corrected and compile-tested.