Java in five minutes
Java is a statically-typed, object-oriented language. Statically-typed means every variable's type is fixed at compile time—you can't suddenly assign a String to a variable holding an int. Object-oriented means code is organised into classes. For DSA you'll rarely build elaborate class hierarchies; you'll mostly use one Solution class with a few methods, exactly like LeetCode's editor expects.
The minimum runnable program
public class Main {
public static void main(String[] args) {
System.out.println("Hello, DSA");
}
}
Variables, conditionals, loops
int x = 10;
double pi = 3.14;
boolean flag = true;
String name = "riya";
if (x > 5) { ... }
else if (x == 5) { ... }
else { ... }
for (int i = 0; i < 10; i++) { ... }
while (x > 0) { x--; }
for (int v : arr) { ... } // enhanced-for
// Ternary — handy for one-liners
int max = a > b ? a : b;
LeetCode method signatures
On LeetCode, you fill in a method body. You don't read input or write output. The platform calls your method and grades the return value. Internalize these shapes:
class Solution {
// "Two Sum"
public int[] twoSum(int[] nums, int target) { ... }
// "Level Order Traversal"
public List<List<Integer>> levelOrder(TreeNode root) { ... }
// "Valid Parentheses"
public boolean isValid(String s) { ... }
}
Local testing (when you do need stdin)
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int[] a = new int[n];
for (int i = 0; i < n; i++) a[i] = sc.nextInt();
System.out.println(Arrays.toString(a));
}
}
Primitives vs wrappers
Java has two kinds of types. Primitives (int, long, double, boolean, char) are raw values stored directly. Wrappers (Integer, Long, Double, Boolean, Character) are objects that wrap a primitive. The annoying rule: collections only hold objects, so a list of integers is List<Integer>, not List<int>.
| Primitive | Bits | Range | Wrapper |
|---|---|---|---|
int | 32 | ±2.1 × 10⁹ | Integer |
long | 64 | ±9.2 × 10¹⁸ | Long |
double | 64 | ~15 decimal digits | Double |
boolean | 1 (logical) | true / false | Boolean |
char | 16 | Unicode code unit | Character |
long. A multiplication of two ints overflows silently—cast one to long first: (long) a * b.
Autoboxing and the == trap
Integer a = 128, b = 128;
System.out.println(a == b); // false — reference compare
System.out.println(a.equals(b)); // true — value compare
// For wrapper objects, always use .equals()
// For primitives, == is the value comparison.
The Collections cheat-sheet
This is the table. Bookmark it. Internalize the imports.
import java.util.*; // imports ALL of the below — fine on LeetCode
// Lists
List<Integer> al = new ArrayList<>();
List<Integer> ll = new LinkedList<>();
// Sets
Set<Integer> hs = new HashSet<>(); // unordered, O(1) ops
Set<Integer> ts = new TreeSet<>(); // sorted, O(log n)
Set<Integer> lhs = new LinkedHashSet<>(); // insertion-order
// Maps
Map<String,Integer> hm = new HashMap<>();
Map<String,Integer> tm = new TreeMap<>();
Map<String,Integer> lhm = new LinkedHashMap<>();
// Queue / Stack / Deque — prefer ArrayDeque
Queue<Integer> q = new ArrayDeque<>();
Deque<Integer> stack = new ArrayDeque<>();
Deque<Integer> deque = new ArrayDeque<>();
// Heap
PriorityQueue<Integer> pq = new PriorityQueue<>();
PriorityQueue<Integer> maxPq = new PriorityQueue<>(Comparator.reverseOrder());
The methods you'll use 90% of the time
| Op | List | Set | Map | Queue/Deque |
|---|---|---|---|---|
| add | add(x) | add(x) | put(k,v) | offer(x) |
| check | contains | contains | containsKey | contains |
| fetch | get(i) | — | get(k) | peek |
| remove | remove(i) | remove(x) | remove(k) | poll |
| size | size() | size() | size() | size() |
Iterating a map — the patterns
// Keys only
for (String k : map.keySet()) { ... }
// Values only
for (int v : map.values()) { ... }
// Both — preferred
for (Map.Entry<String, Integer> e : map.entrySet()) {
String k = e.getKey();
int v = e.getValue();
}
Converting between forms
// int[] → List<Integer>
List<Integer> list = Arrays.stream(arr).boxed().collect(Collectors.toList());
// List<Integer> → int[]
int[] back = list.stream().mapToInt(Integer::intValue).toArray();
// Array → List view (FIXED-SIZE, do not call add/remove)
List<String> view = Arrays.asList("a", "b");
// Set → List
List<Integer> fromSet = new ArrayList<>(set);
// String → char[]
char[] chars = s.toCharArray();
// char[] → String
String back = new String(chars);
Comparator & custom sorting
A Comparator is just a tiny function: "given a and b, which one comes first?" Return a negative number to put a before b, positive for after, zero for equal. Modern Java lets you write this as a one-line lambda. This is the single most useful Java idiom for DSA—every interview problem with custom ordering uses it.
Basics
// Ascending — the default
Arrays.sort(arr);
// Descending — Integer[] not int[]
Arrays.sort(boxedArr, (a, b) -> b - a);
// Safer for big numbers (avoids overflow)
Arrays.sort(boxedArr, (a, b) -> Integer.compare(b, a));
// Sort objects by field
Arrays.sort(intervals, (a, b) -> a[0] - b[0]);
// Multi-key — by first, ties broken by second
Arrays.sort(intervals, (a, b) ->
a[0] != b[0] ? a[0] - b[0] : a[1] - b[1]);
Comparator.comparing — the readable form
// Sort meetings by start, then by end
Arrays.sort(meetings, Comparator
.comparingInt((int[] m) -> m[0])
.thenComparingInt(m -> m[1]));
// Reverse easily
list.sort(Comparator.reverseOrder());
// Custom class
people.sort(Comparator.comparing(Person::getAge).reversed());
b - a overflow trap(a, b) -> b - a looks elegant but breaks when b is very negative and a is very positive — the subtraction overflows. For competitive-style problems with wide ranges, always use Integer.compare(b, a) or Long.compare(...).
Strings & StringBuilder
Strings in Java are immutable. Every "edit" creates a new String. Concatenating in a loop is therefore O(n²) — each += copies the whole string. Use StringBuilder for any string built character-by-character. This single change can drop "Time Limit Exceeded" to "Accepted."
String — the methods that matter
String s = "hello world";
s.length(); // 11
s.charAt(0); // 'h'
s.substring(0, 5); // "hello" — [start, end)
s.indexOf("world"); // 6, or -1
s.contains("ell"); // true
s.startsWith("he"); // true
s.endsWith("ld"); // true
s.toLowerCase();
s.toUpperCase();
s.trim(); // strip leading/trailing whitespace
s.replace('l', 'L'); // "heLLo worLd"
s.split(" "); // ["hello", "world"]
s.toCharArray();
s.equals("hello world"); // content equality
String.valueOf(123); // int → "123"
Integer.parseInt("123"); // "123" → 123
StringBuilder — the workhorse
StringBuilder sb = new StringBuilder();
sb.append("hi");
sb.append(42);
sb.append('!');
sb.insert(0, ">>>"); // insert at index
sb.deleteCharAt(1);
sb.reverse(); // in-place reverse
sb.length();
sb.charAt(2);
sb.setCharAt(2, 'X');
String result = sb.toString();
+ is fine; the compiler optimises it.
Generics
Generics are why you write List<Integer> instead of just List. The angle brackets tell Java what type lives inside the collection, so you get compile-time type-checking and don't have to cast on every get. For DSA, you'll use them mostly to declare collections; you'll rarely write your own generic class.
// Without generics — gross, error-prone
List names = new ArrayList();
names.add("Riya");
String s = (String) names.get(0); // manual cast required
// With generics — clean and type-safe
List<String> names2 = new ArrayList<>();
names2.add("Riya");
String s2 = names2.get(0); // no cast
// The diamond <> — infers the type
Map<String, List<Integer>> index = new HashMap<>();
A simple generic class
class Pair<A, B> {
A first;
B second;
Pair(A a, B b) { first = a; second = b; }
}
Pair<String, Integer> p = new Pair<>("score", 99);
Lambdas & functional bits
A lambda is an anonymous function — a short way to pass behaviour around like data. You've already used them in Comparators. They show up wherever Java expects a single-method interface (called a "functional interface"), and they make your DSA code dramatically shorter.
// Single arg, no parens needed
Function<Integer, Integer> square = x -> x * x;
square.apply(5); // 25
// Two args
BiFunction<Integer, Integer, Integer> add = (a, b) -> a + b;
// Predicate — returns boolean
Predicate<Integer> isEven = n -> n % 2 == 0;
// Consumer — no return
Consumer<String> print = s -> System.out.println(s);
list.forEach(print);
list.forEach(System.out::println); // method reference
Streams — when they help
Streams are a fluent API for transforming collections. Great for one-line transformations, sometimes slower than a plain loop on huge inputs. Use sparingly in DSA—a clear loop usually wins.
// Sum of even squares
int total = Arrays.stream(nums)
.filter(n -> n % 2 == 0)
.map(n -> n * n)
.sum();
// Distinct sorted list
List<Integer> uniq = list.stream()
.distinct()
.sorted()
.collect(Collectors.toList());
Math & numeric edges
Math.max(a, b);
Math.min(a, b);
Math.abs(x);
Math.pow(2, 10); // 1024.0 (double — cast if needed)
Math.sqrt(16); // 4.0
Math.floor(3.7); // 3.0
Math.ceil(3.2); // 4.0
Math.floorDiv(-7, 2); // -4 (vs. -7/2 = -3 in Java)
Math.floorMod(-7, 3); // 2 (always non-negative for + mod)
// Limits
Integer.MAX_VALUE; // 2,147,483,647
Integer.MIN_VALUE;
Long.MAX_VALUE;
-7 % 3 is -1, not 2. For hash-style "wrap to positive" math, use Math.floorMod(-7, 3) or ((x % m) + m) % m. Many graph and grid problems quietly need this.
Top 10 Java pitfalls in DSA
==on Strings / wrappers compares references, not content. Always use.equals().- Array length is
arr.length; String length iss.length(); Collection size islist.size(). Three different forms—memorise them. list.remove(int)ambiguity. On aList<Integer>,remove(2)removes index 2. To remove the value 2, writeremove(Integer.valueOf(2)).- Integer overflow. When inputs near 10⁹ and you're adding or multiplying, switch to
long:long product = (long) a * b;. b - ain a Comparator overflows for very large/small values. PreferInteger.compare(b, a).- Modifying a collection while iterating throws
ConcurrentModificationException. Use anIterator'sremove()or collect-then-remove. - String concatenation in a loop is O(n²). Use
StringBuilder. Arrays.asList(intArr)returns aList<int[]>of size 1, not aList<Integer>. UseArrays.stream(arr).boxed().toList().- Integer division.
5 / 2is2, not2.5. Cast to double for fractional results:(double) 5 / 2. - Forgetting to snapshot in backtracking. Add
new ArrayList<>(current)to the result list, notcurrentitself—or every entry will be the same mutated list.
The snippet vault
Six copy-paste-ready blocks you'll reach for in interview after interview.
Frequency map of a String
Map<Character, Integer> freq = new HashMap<>();
for (char c : s.toCharArray())
freq.merge(c, 1, Integer::sum);
Top-K with a min-heap
PriorityQueue<Integer> pq = new PriorityQueue<>();
for (int n : nums) {
pq.offer(n);
if (pq.size() > k) pq.poll();
}
// pq now holds the k largest values; pq.peek() is the kth-largest
Grid 4-direction BFS
int[][] D = {{-1,0},{1,0},{0,-1},{0,1}};
for (int[] d : D) {
int nr = r + d[0], nc = c + d[1];
if (nr < 0 || nr >= R || nc < 0 || nc >= C) continue;
// process (nr, nc)
}
Reverse a linked list
ListNode prev = null, curr = head;
while (curr != null) {
ListNode next = curr.next;
curr.next = prev;
prev = curr;
curr = next;
}
return prev;
Two-pointer template (sorted array, pair sum)
int l = 0, r = nums.length - 1;
while (l < r) {
int sum = nums[l] + nums[r];
if (sum == target) return new int[]{l, r};
if (sum < target) l++; else r--;
}
Tree DFS skeleton
int dfs(TreeNode node) {
if (node == null) return 0; // base case
int left = dfs(node.left);
int right = dfs(node.right);
// combine with node.val
return Math.max(left, right) + 1;
}
You now have complexity, structures, patterns, and Java. The rest is hours at the keyboard.