Why this topic matters
Almost every program manipulates groups of values: a list of users returned from a database query, a set of permissions, a map from ID to entity, a queue of work items. The Collections Framework provides a small, well-designed set of interfaces (List, Set, Map, Queue) and a richer set of implementations (ArrayList, LinkedList, HashSet, TreeSet, HashMap, TreeMap, ArrayDeque, PriorityQueue) for those interfaces. Knowing which to choose for a given job is one of the most practically useful skills in Java.
Picking the right collection is also a performance decision. contains on an ArrayList of 100,000 elements is O(n) — 100,000 comparisons in the worst case. The same lookup on a HashSet is O(1). Switching a list to a set in a hot loop can produce a 10x speedup with no other change. Conversely, using a LinkedList where an ArrayList would do costs you memory and cache locality, even if the big-O complexity is the same.
The collections framework is also where most Java interviews probe. “Explain the collections hierarchy” is in our interview questions for a reason — it is asked at almost every Java technical screen. Knowing the difference between ArrayList and LinkedList, the difference between HashMap and TreeMap, and the contract between equals and hashCode will see you through most collections-related interview questions.
What you will learn
You will learn how to choose the right collection for a job: ArrayList for indexed access, LinkedList for frequent insertions at the ends (rarely the right choice in practice), HashSet for fast uniqueness, TreeSet for sorted uniqueness, HashMap for key-value lookup, TreeMap for sorted key-value lookup, and ArrayDeque / PriorityQueue for ordering and priority processing. You will also learn how to iterate, sort, filter and transform collections both with classic loops and with streams.
You will also learn the often-overlooked immutability factories — List.of, Set.of, Map.of — added in Java 9. These produce immutable collections in one line and are usually the right choice for fixed-size data. The mutable ArrayList and HashMap are still the right choice when you need to add or remove elements after construction.
Core concepts
- Collection<E> — the root interface for List, Set, and Queue (but not Map).
- Iterable<E> — the parent of Collection; anything that can be iterated with a for-each loop.
- List<E> — ordered, allows duplicates, indexed access. Implementations:
ArrayList,LinkedList. - Set<E> — no duplicates. Implementations:
HashSet,TreeSet,LinkedHashSet. - Map<K,V> — key-value pairs, unique keys. Implementations:
HashMap,TreeMap,LinkedHashMap. - Queue / Deque — ordered for FIFO / double-ended. Implementations:
ArrayDeque,LinkedList,PriorityQueue. - Iterator — the classic iteration interface; supports safe
remove()during iteration. - Comparator — an external ordering strategy;
Comparableis internal. - Immutable factories —
List.of,Set.of,Map.of,List.copyOf.
Common pitfalls
- Modifying while iterating. Calling
list.remove(x)in a for-each throwsConcurrentModificationException. Useiterator.remove()orlist.removeIf(...). - Mutating a list returned from a method. The caller may have cached the original list; mutating it breaks their expectations. Return
List.copyOf(...)instead. - Using
LinkedListfor indexed access.LinkedList.get(i)is O(n). UseArrayListfor indexed access. - Forgetting
equals/hashCodeon Map keys. AHashMapwith key objects that do not override these methods will misplace entries. - Boxing in tight loops.
ArrayList<Integer>boxes everyint. For large primitive collections, use a primitive specialisation library like Eclipse Collections.
Best practices
- Program to the interface, not the implementation:
List<String> list = new ArrayList<>(); - Use immutable factories (
List.of,Set.of,Map.of) for fixed-size data. - Estimate the expected size when constructing
ArrayListorHashMap— pass it to the constructor to avoid resizes. - Prefer
ArrayDequeoverLinkedListfor stacks and queues — it is faster and uses less memory. - Use
EnumSetandEnumMapfor enum-keyed collections — they are dramatically more efficient than the general-purpose alternatives. - When in doubt, benchmark. The performance characteristics matter in hot code; for cold code, clarity is more important than micro-optimisation.
Frequently asked questions
What is the difference between ArrayList and LinkedList?
ArrayList is backed by a dynamically-resized array; LinkedList is backed by a doubly-linked list. In practice, ArrayList is almost always the better choice — its contiguous memory layout is friendlier to CPU caches, and indexed access is O(1) vs LinkedList's O(n). LinkedList only wins at frequent insertions in the middle of the list, which is a rare pattern. Most working Java developers never use LinkedList.
Why does HashMap use a tree internally?
Since Java 8, HashMap converts long collision chains (more than 8 entries) into a balanced tree. This turns a pathological worst-case of O(n) lookup, where an attacker could hash-collide keys to denial-of-service a server, into O(log n). The conversion threshold and the tree structure are tunable, but the defaults are good for almost all use cases.
Should I use List.of or Arrays.asList?
List.of (Java 9+) returns an immutable list that rejects nulls and structural modifications. Arrays.asList returns a fixed-size view of the array that allows set but not add or remove, and accepts nulls. Use List.of for immutable constant data; use new ArrayList<>(Arrays.asList(...)) if you need a mutable copy.
What is a fail-fast iterator?
A fail-fast iterator throws ConcurrentModificationException if the underlying collection is modified after the iterator is created, except via the iterator's own remove() method. The iterators on ArrayList, HashMap, and most of the framework are fail-fast. This is a debugging aid — it surfaces concurrent modification bugs early rather than producing undefined behaviour later.