What is the difference between List, Set, and Map in Java?
Table of Contents
1. Short Answer
Java's Collection Framework organises its interfaces into three main groups:
- List — ordered, index-based, allows duplicates. Primary implementation:
ArrayList. - Set — unordered (or sorted with
TreeSet), no duplicates. Primary implementation:HashSet. - Map — key-value pairs, keys are unique, values can repeat. Primary implementation:
HashMap. (Map does not extendCollection.)
Interview One-Liner
List = ordered + duplicates allowed; Set = unique elements; Map = key → value lookup. This distinction is asked in almost every Java interview.
2. Detailed Explanation
2.1 List Interface
The java.util.List interface extends Collection and represents an ordered sequence of elements. Key characteristics:
- Elements maintain their insertion order.
- Duplicate values are permitted — the same object can appear multiple times.
- Elements are accessible by zero-based integer index via
get(int index). - Allows
nullvalues (multiple nulls inArrayList).
Main implementations:
ArrayList— backed by a dynamic array; O(1) random access, O(n) insert/delete in the middle.LinkedList— doubly-linked list; O(1) insert/delete at ends, O(n) random access.Vector— legacy, synchronized version of ArrayList.CopyOnWriteArrayList— thread-safe variant for concurrent reads.
2.2 Set Interface
The java.util.Set interface extends Collection and models a mathematical set — a group with no duplicate members. Key characteristics:
- No duplicate elements;
add()returnsfalseif the element already exists. - Uniqueness is determined by
hashCode()andequals(). - Most implementations (HashSet) do not guarantee insertion order.
- At most one
nullis allowed (inHashSet).
Main implementations:
HashSet— hash table; O(1) average add/contains/remove.LinkedHashSet— HashSet + doubly-linked list; maintains insertion order.TreeSet— Red-Black tree; O(log n) operations; elements sorted naturally or byComparator.EnumSet— ultra-fast bit-vector implementation forenumtypes.
2.3 Map Interface
The java.util.Map interface is not a subtype of Collection. It maps unique keys to values. Key characteristics:
- Keys must be unique; duplicate keys overwrite the existing value.
- Values can be duplicated.
- Provides three views:
keySet(),values(),entrySet(). - One
nullkey allowed inHashMap;TreeMapdoes not allow null keys.
Main implementations:
HashMap— hash table; O(1) average get/put.LinkedHashMap— HashMap + linked list; maintains insertion order.TreeMap— Red-Black tree; O(log n); keys in sorted order.Hashtable— legacy, synchronized; does not allow null keys/values.ConcurrentHashMap— thread-safe, high-concurrency HashMap.
3. Code Example
import java.util.*;
public class CollectionDemo {
public static void main(String[] args) {
// ---- LIST ----
List<String> list = new ArrayList<>();
list.add("Apple");
list.add("Banana");
list.add("Apple"); // duplicate allowed
list.add(null); // null allowed
System.out.println("List: " + list);
// Output: [Apple, Banana, Apple, null]
System.out.println("Element at index 1: " + list.get(1)); // Banana
// ---- SET ----
Set<String> set = new HashSet<>();
set.add("Apple");
set.add("Banana");
boolean added = set.add("Apple"); // returns false — duplicate ignored
System.out.println("Set: " + set); // [Banana, Apple] (order not guaranteed)
System.out.println("Apple added again? " + added); // false
// LinkedHashSet preserves insertion order
Set<String> linked = new LinkedHashSet<>(list);
System.out.println("LinkedHashSet: " + linked); // [Apple, Banana, null]
// TreeSet sorts elements
Set<String> sorted = new TreeSet<>(Arrays.asList("Cherry", "Apple", "Banana"));
System.out.println("TreeSet: " + sorted); // [Apple, Banana, Cherry]
// ---- MAP ----
Map<String, Integer> map = new HashMap<>();
map.put("Alice", 90);
map.put("Bob", 85);
map.put("Alice", 95); // overwrites previous value for key "Alice"
System.out.println("Map: " + map); // {Alice=95, Bob=85}
System.out.println("Alice's score: " + map.get("Alice")); // 95
// Iterating a Map
for (Map.Entry<String, Integer> entry : map.entrySet()) {
System.out.println(entry.getKey() + " -> " + entry.getValue());
}
// Map.getOrDefault — safe retrieval
int score = map.getOrDefault("Charlie", 0);
System.out.println("Charlie's score: " + score); // 0
}
}
Note on null
TreeSet and TreeMap throw NullPointerException if you try to add/put a null key because they compare elements for ordering. HashSet and HashMap allow one null key.
4. Time Complexity Comparison
| Operation | ArrayList | HashSet | HashMap | TreeSet / TreeMap |
|---|---|---|---|---|
| Add / Put | O(1) amortized | O(1) avg | O(1) avg | O(log n) |
| Get by Index / Key | O(1) | N/A | O(1) avg | O(log n) |
| Contains / ContainsKey | O(n) | O(1) avg | O(1) avg | O(log n) |
| Remove | O(n) | O(1) avg | O(1) avg | O(log n) |
| Iteration | O(n) | O(n) | O(n) | O(n) |
| Ordered? | Yes (insertion) | No | No | Yes (sorted) |
| Duplicates? | Yes | No | Keys: No, Values: Yes | No |
Hash-based structures have O(n) worst-case complexity if many keys collide, but this is extremely rare with a good hashCode() implementation.
5. When to Use Each
5.1 Use List When
- You need to preserve the order elements were inserted.
- Duplicate values are expected or required (e.g., shopping cart items).
- You need index-based access (
get(2)). - You are building a stack, queue, or ordered sequence.
5.2 Use Set When
- You need to enforce uniqueness (e.g., unique usernames, visited URLs).
- You need fast membership testing (
contains()). - You need mathematical set operations (union, intersection) — use
retainAll(),addAll(). - Order does not matter (or use
TreeSet/LinkedHashSetwhen it does).
5.3 Use Map When
- You need to associate a value with a unique key (dictionary, cache).
- You need fast key-based lookup without scanning all elements.
- Examples: word frequency count, student grades by name, config settings.
- Use
TreeMapwhen you need the keys in sorted order.
Quick Decision Rule
Ask yourself: do I need duplicates? (List) Do I only need unique values? (Set) Do I need to look something up by a key? (Map).
6. Interview Context
This question tests whether a candidate understands the Java Collections hierarchy and can choose the right data structure for a problem. Interviewers often follow up with:
- "If I need to count word frequencies in a document, which would you use?" →
HashMap<String, Integer> - "How do you remove duplicates from a List?" → Convert to
HashSet(loses order) orLinkedHashSet(preserves order). - "Can a Map have duplicate values?" → Yes, only keys must be unique.
- "What happens if you add a duplicate to a HashSet?" →
add()returnsfalse, set is unchanged. - "Why does Map not extend Collection?" → Because Map models key-value associations, not a sequence of individual elements. The interface contract is fundamentally different.
7. Common Pitfalls
7.1 Mutating Keys in a HashMap
If a key object's hashCode() changes after it is inserted, the entry becomes unreachable.
// BAD: using a mutable list as a Map key
List<String> key = new ArrayList<>(Arrays.asList("a", "b"));
Map<List<String>, Integer> map = new HashMap<>();
map.put(key, 1);
key.add("c"); // hashCode of key changed!
System.out.println(map.get(key)); // null — entry lost!
// GOOD: use immutable keys (String, Integer, record, etc.)
Map<String, Integer> safe = new HashMap<>();
safe.put("ab", 1);
7.2 Forgetting to Override equals/hashCode
Custom objects used in a HashSet or as HashMap keys must override both hashCode() and equals(). Without this, two logically equal objects are treated as different.
class Point {
int x, y;
Point(int x, int y) { this.x = x; this.y = y; }
@Override
public boolean equals(Object o) {
if (!(o instanceof Point)) return false;
Point p = (Point) o;
return x == p.x && y == p.y;
}
@Override
public int hashCode() {
return Objects.hash(x, y);
}
}
Set<Point> points = new HashSet<>();
points.add(new Point(1, 2));
System.out.println(points.contains(new Point(1, 2))); // true (with overrides)
7.3 ConcurrentModificationException
Modifying a collection while iterating it with a for-each loop throws ConcurrentModificationException. Use Iterator.remove() or collect elements to remove separately.
List<String> list = new ArrayList<>(Arrays.asList("a", "b", "c"));
// BAD
// for (String s : list) { if (s.equals("b")) list.remove(s); } // throws CME
// GOOD
Iterator<String> it = list.iterator();
while (it.hasNext()) {
if (it.next().equals("b")) it.remove(); // safe
}
System.out.println(list); // [a, c]
8. Conclusion
List, Set, and Map serve distinct purposes in Java's Collections Framework:
- List — ordered sequences with duplicates; use
ArrayListfor most cases. - Set — unique elements; use
HashSetfor speed,LinkedHashSetfor order,TreeSetfor sorting. - Map — key-to-value associations; use
HashMapfor speed,TreeMapfor sorted keys,LinkedHashMapfor insertion-order iteration.
Choosing the right interface up front leads to cleaner code and better performance. In interviews, demonstrate understanding by tying your choice to concrete requirements, not just naming the class.