Menu
CoddyTech

Java Collections Interview Questions

How HashMap, ArrayList and the other collections work inside, and which one to pick.

12 questions
  1. Explain the Java Collections Framework hierarchy.

    Collection branches into List (ordered, duplicates allowed: ArrayList), Set (no duplicates: HashSet, TreeSet) and Queue/Deque (ArrayDeque, PriorityQueue). Map (HashMap, TreeMap) stores key-value pairs and does not extend Collection.

    The legacy Vector, Stack and Hashtable are replaced by ArrayList, ArrayDeque and HashMap.

  2. What is the difference between an array and an ArrayList in Java?

    An array has a fixed length and can hold primitives; an ArrayList grows but holds only objects, so int values are boxed.

    Java
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.List;
    
    public class Main {
        public static void main(String[] args) {
            int[] arr = {3, 1, 2};
            List<Integer> list = new ArrayList<>(List.of(3, 1, 2));
            list.add(4);            // an array cannot grow
            Arrays.sort(arr);
            list.sort(null);
            System.out.println(Arrays.toString(arr) + " length " + arr.length);
            System.out.println(list + " size " + list.size());
        }
    }

    It prints [1, 2, 3] length 3 and [1, 2, 3, 4] size 4. In OpenJDK a full ArrayList copies itself into an array about 1.5 times bigger; the spec only promises that appending is O(1) amortized. See ArrayList.

  3. What is the difference between ArrayList and LinkedList?

    ArrayList is a resizable array with O(1) get(i); LinkedList is a doubly linked list with O(n) get(i) and two pointers per node. Use ArrayList by default.

    Inserting in the middle is O(n) for both, because the linked list must first walk to the position, and arrays are far more cache friendly. For queues and stacks use ArrayDeque.

  4. What does list.remove(1) do on a List<Integer>?

    Java
    import java.util.ArrayList;
    import java.util.List;
    
    public class Main {
        public static void main(String[] args) {
            List<Integer> list = new ArrayList<>(List.of(1, 2, 3));
            list.remove(1);
            System.out.println(list);
        }
    }

    It prints [1, 3]. List<Integer> has remove(int index) and remove(Object o), and the literal 1 is an int, so the call removes index 1, the value 2.

    To remove the value 1, write list.remove(Integer.valueOf(1)).

  5. How does HashMap work internally in Java?

    A HashMap is an array of buckets. put spreads hashCode() (h ^ (h >>> 16)), picks bucket hash & (capacity - 1) and chains colliding entries; lookups walk the chain with equals().

    The table (16 buckets by default) doubles when it passes 75% full. Since Java 8 a chain longer than 8 becomes a red-black tree once the table has 64 buckets (a smaller table resizes instead). One null key is allowed, in bucket 0. See the hash map visualization.

    Practice it: Two Sum, Group Anagrams

  6. What is the difference between HashMap, Hashtable and ConcurrentHashMap?

    HashMap is not thread safe and allows one null key. Hashtable locks the whole table on every method and is legacy. ConcurrentHashMap reads without locks and locks single bins on writes; it allows no nulls, and its iterators never throw ConcurrentModificationException.

    Use its atomic merge or compute: a get followed by a put is still a race.

  7. How does HashSet work internally?

    A HashSet is a HashMap whose keys are the elements and whose values are one shared dummy object; add returns false when the key already exists.

    So it has O(1) average add and contains, no order, and needs consistent equals() and hashCode(). LinkedHashSet keeps insertion order, TreeSet sorted order.

    Practice it: Contains Duplicate, Intersection of Two Arrays

  8. How many equal objects does a HashSet keep: plain classes vs records?

    Java
    import java.util.HashSet;
    import java.util.Set;
    
    class PointClass {
        int x, y;
        PointClass(int x, int y) { this.x = x; this.y = y; }
    }
    
    record PointRecord(int x, int y) {}
    
    public class Main {
        public static void main(String[] args) {
            Set<PointClass> a = new HashSet<>();
            a.add(new PointClass(1, 2));
            a.add(new PointClass(1, 2));
    
            Set<PointRecord> b = new HashSet<>();
            b.add(new PointRecord(1, 2));
            b.add(new PointRecord(1, 2));
    
            System.out.println(a.size() + " " + b.size());
        }
    }

    It prints 2 1. PointClass keeps Object's identity-based equals() and hashCode(), so both objects stay in the set. A record generates both from its components, so the second add is a duplicate.

  9. What is the difference between HashMap, LinkedHashMap and TreeMap?

    Iteration order: HashMap guarantees none, LinkedHashMap keeps insertion order, and TreeMap keeps keys sorted.

    Java
    import java.util.HashMap;
    import java.util.LinkedHashMap;
    import java.util.Map;
    import java.util.TreeMap;
    
    public class Main {
        public static void main(String[] args) {
            String[] keys = {"banana", "apple", "cherry"};
            Map<String, Integer> hash = new HashMap<>();
            Map<String, Integer> linked = new LinkedHashMap<>();
            Map<String, Integer> tree = new TreeMap<>();
            for (String k : keys) {
                hash.put(k, k.length());
                linked.put(k, k.length());
                tree.put(k, k.length());
            }
            System.out.println("linked: " + linked.keySet());
            System.out.println("tree:   " + tree.keySet());
            System.out.println("hash has " + hash.size() + " keys, order not guaranteed");
        }
    }

    linked prints [banana, apple, cherry] and tree prints [apple, banana, cherry]. TreeMap costs O(log n) instead of O(1), but adds floorKey() and range views.

  10. What is a fail-fast iterator? How do you remove elements while iterating?

    A fail-fast iterator throws ConcurrentModificationException when the collection changes structurally other than through the iterator; it checks a modCount counter on each next(). It detects bugs and gives no thread safety. Remove with removeIf or Iterator.remove():

    Java
    import java.util.ArrayList;
    import java.util.Iterator;
    import java.util.List;
    
    public class Main {
        public static void main(String[] args) {
            List<Integer> a = new ArrayList<>(List.of(1, 2, 3, 4, 5, 6));
            a.removeIf(n -> n % 2 == 0);
            System.out.println(a);
    
            List<Integer> b = new ArrayList<>(List.of(1, 2, 3, 4, 5, 6));
            Iterator<Integer> it = b.iterator();
            while (it.hasNext()) {
                if (it.next() > 3) it.remove();
            }
            System.out.println(b);
        }
    }

    It prints [1, 3, 5] and [1, 2, 3]. Concurrent collections have weakly consistent iterators that never throw.

  11. What happens when you remove an element inside a for-each loop?

    Java
    import java.util.ArrayList;
    import java.util.List;
    
    public class Main {
        public static void main(String[] args) {
            List<Integer> list = new ArrayList<>(List.of(1, 2, 3));
            for (Integer x : list) {
                if (x == 2) list.remove(x);
            }
            System.out.println(list);
        }
    }

    It prints [1, 3] with no exception. After removing 2, the size and the iterator's cursor are both 2, so hasNext() is false and the modCount check in next() never runs.

    Removing 1 or 3 throws ConcurrentModificationException, so a passing test proves nothing here; use removeIf.

  12. What is the difference between Comparable and Comparator?

    Comparable is a class's own natural order (compareTo); a Comparator is an external order, so one class can have many.

    Java
    import java.util.ArrayList;
    import java.util.Comparator;
    import java.util.List;
    
    record Student(String name, int marks) implements Comparable<Student> {
        @Override
        public int compareTo(Student other) {
            return name.compareTo(other.name);   // natural order: by name
        }
    }
    
    public class Main {
        public static void main(String[] args) {
            List<Student> list = new ArrayList<>(List.of(
                new Student("Ravi", 82), new Student("Anu", 91), new Student("Kiran", 82)));
    
            list.sort(null);   // natural order
            System.out.println(list.stream().map(Student::name).toList());
    
            list.sort(Comparator.comparingInt(Student::marks).reversed()
                                .thenComparing(Student::name));
            System.out.println(list.stream().map(Student::name).toList());
        }
    }

    It prints [Anu, Kiran, Ravi] twice: Anu has the top mark and the two 82s tie-break by name. Avoid a - b comparators, which can overflow; use Integer.compare.

    Practice it: Merge Intervals

Coddy programming languages illustration

Learn to code with Coddy

Get started