Java Collections Interview Questions

Collections questions appear in almost every Java interview. Interviewers use them to gauge whether you understand data structures, can choose the right tool for a problem, and know what’s happening under the hood.

These ten questions cover the topics that come up most often. Each includes the kind of answer that demonstrates real understanding, not just memorized definitions.

1. What’s the difference between ArrayList and LinkedList?

Both implement the List interface, but they store data differently.

ArrayList uses a resizable array internally. Elements sit in contiguous memory. Accessing any element by index takes constant time, O(1). But inserting or removing elements in the middle requires shifting everything after that position, which takes O(n) time.

LinkedList uses a doubly-linked structure. Each element points to its neighbors. Accessing an element by index requires traversing from the start or end, taking O(n) time. But once you’re at a position, inserting or removing takes O(1) time since you’re just updating pointers.

// ArrayList - fast random access
ArrayList<String> arrayList = new ArrayList<>();
arrayList.add("first");
arrayList.add("second");
String item = arrayList.get(1);  // O(1) - direct index access

// LinkedList - fast insertion/removal at known positions
LinkedList<String> linkedList = new LinkedList<>();
linkedList.add("first");
linkedList.addFirst("new first");  // O(1) - just update pointers

When to use which: ArrayList for most cases, especially when you access elements by index frequently. LinkedList when you’re constantly adding or removing from the beginning or middle, like implementing a queue.

2. How does HashMap work internally?

HashMap stores key-value pairs using an array of buckets. When you put a key-value pair, HashMap computes the key’s hash code, converts it to an array index, and stores the entry in that bucket.

Multiple keys can hash to the same bucket. This is called a collision. Before Java 8, collisions were handled with linked lists. Each bucket contained a chain of entries. Java 8 changed this: when a bucket exceeds 8 entries, it converts to a balanced tree for O(log n) lookup instead of O(n).

HashMap<String, Integer> scores = new HashMap<>();
scores.put("Alice", 95);    // hashCode("Alice") determines bucket
scores.put("Bob", 87);
scores.put("Charlie", 92);

// Retrieval
int aliceScore = scores.get("Alice");  // O(1) average case

Follow-up interviewers often ask: What happens when the HashMap gets too full? When the load factor (default 0.75) is exceeded, HashMap doubles its capacity and rehashes all entries. This is expensive but happens infrequently.

3. What’s the difference between HashMap and Hashtable?

Hashtable is a legacy class from Java 1.0. HashMap replaced it in Java 1.2. The differences:

Synchronization: Hashtable is synchronized. Every method acquires a lock. HashMap is not synchronized and therefore faster in single-threaded contexts.

Null handling: HashMap allows one null key and any number of null values. Hashtable throws NullPointerException if you try to store null.

Iteration: Hashtable uses Enumeration. HashMap uses Iterator, which supports the remove() operation during iteration.

// HashMap allows null
HashMap<String, String> map = new HashMap<>();
map.put(null, "value for null key");  // Works fine
map.put("key", null);                  // Also fine

// Hashtable does not
Hashtable<String, String> table = new Hashtable<>();
table.put(null, "value");  // Throws NullPointerException

Best practice: Use HashMap. If you need thread safety, use ConcurrentHashMap instead of Hashtable. It provides better performance through segment-level locking.

4. How do HashSet and TreeSet differ?

Both store unique elements, but their internal implementations differ.

HashSet uses a HashMap internally (the elements become keys, with a dummy value). It provides O(1) average time for add, remove, and contains operations. Elements have no guaranteed order.

TreeSet uses a red-black tree (specifically, a TreeMap). It maintains elements in sorted order. Operations take O(log n) time. Elements must be Comparable or you must provide a Comparator.

// HashSet - no order guaranteed
HashSet<String> hashSet = new HashSet<>();
hashSet.add("banana");
hashSet.add("apple");
hashSet.add("cherry");
// Iteration order: unpredictable

// TreeSet - sorted order
TreeSet<String> treeSet = new TreeSet<>();
treeSet.add("banana");
treeSet.add("apple");
treeSet.add("cherry");
// Iteration order: apple, banana, cherry

There’s also LinkedHashSet, which maintains insertion order while keeping O(1) operations. Use it when you need uniqueness plus predictable iteration order.

5. What is the difference between Comparable and Comparator?

Both enable sorting, but they serve different purposes.

Comparable defines the natural ordering of a class. You implement it in the class itself by overriding compareTo(). A class can have only one natural ordering.

public class Employee implements Comparable<Employee> {
    private String name;
    private int salary;

    @Override
    public int compareTo(Employee other) {
        return this.name.compareTo(other.name);  // Natural order by name
    }
}

Comparator defines external, alternative orderings. You can create multiple Comparators for the same class without modifying it.

// Sort by salary instead of name
Comparator<Employee> bySalary = (e1, e2) -> 
    Integer.compare(e1.getSalary(), e2.getSalary());

// Sort by name length
Comparator<Employee> byNameLength = (e1, e2) -> 
    Integer.compare(e1.getName().length(), e2.getName().length());

// Usage
Collections.sort(employees, bySalary);
employees.sort(byNameLength);

Rule of thumb: Implement Comparable for the obvious, default ordering. Use Comparator for alternative orderings or when you can’t modify the class.

6. What happens if you modify a collection while iterating over it?

You get a ConcurrentModificationException. The iterator detects that the collection changed and fails fast.

List<String> names = new ArrayList<>(Arrays.asList("Alice", "Bob", "Charlie"));

// This throws ConcurrentModificationException
for (String name : names) {
    if (name.startsWith("B")) {
        names.remove(name);  // Modifying during iteration
    }
}

Safe alternatives:

// Option 1: Use Iterator's remove method
Iterator<String> iterator = names.iterator();
while (iterator.hasNext()) {
    String name = iterator.next();
    if (name.startsWith("B")) {
        iterator.remove();  // Safe removal
    }
}

// Option 2: Use removeIf (Java 8+)
names.removeIf(name -> name.startsWith("B"));

// Option 3: Collect items to remove, then remove after iteration
List<String> toRemove = new ArrayList<>();
for (String name : names) {
    if (name.startsWith("B")) {
        toRemove.add(name);
    }
}
names.removeAll(toRemove);

7. When would you use a LinkedHashMap?

LinkedHashMap extends HashMap but maintains a doubly-linked list through all entries. This preserves either insertion order or access order.

Insertion order mode (default) remembers when you added each entry:

LinkedHashMap<String, Integer> insertionOrder = new LinkedHashMap<>();
insertionOrder.put("first", 1);
insertionOrder.put("second", 2);
insertionOrder.put("third", 3);

// Iteration follows insertion order: first, second, third
for (String key : insertionOrder.keySet()) {
    System.out.println(key);
}

Access order mode moves entries to the end when accessed. This is useful for building LRU (Least Recently Used) caches:

// true = access order mode
LinkedHashMap<String, Integer> accessOrder = new LinkedHashMap<>(16, 0.75f, true);
accessOrder.put("a", 1);
accessOrder.put("b", 2);
accessOrder.put("c", 3);

accessOrder.get("a");  // "a" moves to end

// Iteration order now: b, c, a

You can override removeEldestEntry() to automatically evict the oldest entry when the map exceeds a certain size. Perfect for caching.

8. What’s the difference between fail-fast and fail-safe iterators?

Fail-fast iterators throw ConcurrentModificationException if the collection is modified during iteration. ArrayList, HashMap, and most standard collections use fail-fast iterators. They check a modification count and fail immediately when it changes.

Fail-safe iterators work on a copy of the collection or use special mechanisms to handle concurrent modification. They don’t throw exceptions but may not reflect recent changes. ConcurrentHashMap and CopyOnWriteArrayList use fail-safe iterators.

// Fail-fast (ArrayList)
List<String> list = new ArrayList<>(Arrays.asList("a", "b", "c"));
for (String s : list) {
    list.add("d");  // ConcurrentModificationException
}

// Fail-safe (CopyOnWriteArrayList)
List<String> safeList = new CopyOnWriteArrayList<>(Arrays.asList("a", "b", "c"));
for (String s : safeList) {
    safeList.add("d");  // No exception, but iterator won't see "d"
}

Fail-safe comes with overhead. CopyOnWriteArrayList copies the entire array on every write. Use it only when reads vastly outnumber writes.

9. How do you make a collection thread-safe?

Several approaches exist, each with tradeoffs.

Collections.synchronizedXxx() wrappers: Wraps any collection with synchronized methods. Simple but coarse-grained locking hurts performance.

List<String> syncList = Collections.synchronizedList(new ArrayList<>());
Map<String, Integer> syncMap = Collections.synchronizedMap(new HashMap<>());

// Must synchronize manually during iteration
synchronized (syncList) {
    for (String item : syncList) {
        // process item
    }
}

Concurrent collections: Purpose-built for multithreaded access. ConcurrentHashMap uses segment locking for better parallelism. CopyOnWriteArrayList works well for read-heavy workloads.

ConcurrentHashMap<String, Integer> concurrentMap = new ConcurrentHashMap<>();
concurrentMap.put("key", 1);
concurrentMap.computeIfAbsent("newKey", k -> expensiveComputation());

CopyOnWriteArrayList<String> cowList = new CopyOnWriteArrayList<>();
// Safe iteration without external synchronization

Best practice: Prefer concurrent collections over synchronized wrappers. They’re designed for concurrent access and perform better under contention.

10. What is the Collections class and how does it differ from Collection?

Collection (singular) is an interface. It’s the root of the collection hierarchy, extended by List, Set, and Queue.

Collections (plural) is a utility class with static methods for operating on collections. Think of it as a toolbox.

// Collection - the interface
Collection<String> items = new ArrayList<>();
items.add("one");
items.size();
items.isEmpty();

// Collections - utility class with static methods
List<Integer> numbers = new ArrayList<>(Arrays.asList(3, 1, 4, 1, 5));

Collections.sort(numbers);           // Sort in place
Collections.reverse(numbers);        // Reverse order
Collections.shuffle(numbers);        // Random order
int max = Collections.max(numbers);  // Find maximum
int freq = Collections.frequency(numbers, 1);  // Count occurrences

// Create immutable/synchronized views
List<String> immutable = Collections.unmodifiableList(myList);
List<String> synced = Collections.synchronizedList(myList);

Common interview trap: Confusing the two. Remember that one is an interface (Collection), the other is a helper class (Collections).

Preparation Tips

Knowing definitions isn’t enough. Interviewers want to see that you can apply this knowledge. Practice explaining when you’d choose one collection over another. Be ready to discuss time complexity for common operations.

Draw out the internal structures if asked. A quick sketch of how HashMap buckets work or how a TreeSet maintains balance shows deeper understanding than reciting facts.

And don’t forget the Java 8+ additions. Streams, forEach, computeIfAbsent, and other modern methods come up frequently. Interviewers notice when candidates are stuck in older Java idioms.


Related: 10 Java Interview Questions Every Entry-Level Developer Should Know | OOP Interview Questions in Java | Java Collections Framework Overview

Sources

  • Oracle. “Java Collections Framework.” docs.oracle.com
  • Bloch, Joshua. “Effective Java.” 3rd Edition, 2018
  • Oracle. “HashMap (Java SE 21).” docs.oracle.com
Scroll to Top