Java Collections - List, ArrayList & LinkedList Interview Questions (2026) Interview Questions | JiQuest

add

#

Java Collections - List, ArrayList & LinkedList Interview Questions (2026)

BACKEND INTERVIEW PREPARATION
Java Collections: List, ArrayList & LinkedList
Master 100+ essential interview questions on the Java Collections framework — covering ArrayList internals, LinkedList as Deque, time complexity comparisons, concurrency utilities, immutable lists, iterators, and more. Essential reading for Java backend roles in 2026.
⏳ 50 min read 📝 100+ Q&As 🎯 Beginner to Advanced
⚡ Quick Reference
ArrayListDynamic array backed by Object[]; default capacity 10; grows by 1.5x
LinkedListDoubly linked list; also implements Deque; no random access in O(1)
List.of() (Java 9+)Truly immutable; no nulls; no structural or value modification
Arrays.asList()Fixed-size, set() allowed, add/remove throws UnsupportedOperationException
CopyOnWriteArrayListThread-safe; copy-on-write semantics; iterator never throws CME
Fail-fast iteratorThrows ConcurrentModificationException if modCount changes during iteration
ListIteratorBidirectional; supports add/set/remove during iteration
subList()Returns a view backed by original list; modifications reflect in both
Collections.sort()Uses TimSort; O(n log n); stable sort algorithm
List.copyOf() (Java 10)Truly immutable shallow copy; rejects nulls
Java Collection Hierarchy (List Branch)
Iterable<E>
Collection<E>
List<E>
ArrayList
LinkedList
Vector → Stack
CopyOnWriteArrayList
Set<E>
HashSet
TreeSet
Queue<E>
PriorityQueue
Deque → ArrayDeque
LinkedList implements both List and Deque

Java Collections — List, ArrayList & LinkedList Interview Questions & Answers

Q1. What is the Java Collections Framework?

A: The Java Collections Framework (JCF) is a unified architecture for representing and manipulating groups of objects. It includes interfaces (Collection, List, Set, Queue, Map), concrete implementations (ArrayList, LinkedList, HashSet, HashMap, etc.), and utility classes (Collections, Arrays). It provides generic, reusable data structures that eliminate the need to write custom data structure code.

Q2. Describe the Collection hierarchy starting from Iterable.

A: The root interface is Iterable<E>, which enables enhanced for-loop support by declaring the iterator() method. Collection<E> extends Iterable and adds methods like add(), remove(), contains(), size(), isEmpty(), clear(). Below Collection: List (ordered, allows duplicates, index-based access), Set (no duplicates), Queue (FIFO ordering), and Deque (double-ended queue). Map is separate — it does not extend Collection.

Q3. What is the internal data structure of ArrayList?

A: ArrayList is backed by a plain Java array — specifically Object[] elementData. All elements are stored in this array. When you call new ArrayList() in Java 8+, elementData is set to a shared empty array constant (DEFAULTCAPACITY_EMPTY_ELEMENTDATA), and the first add triggers lazy initialization to capacity 10. The array grows when needed.

Q4. What is the default initial capacity of ArrayList?

A: The default initial capacity is 10. When you create new ArrayList() without specifying a capacity, the backing array starts as an empty array and lazily allocates space for 10 elements on the first add. You can pass a custom initial capacity: new ArrayList<>(50) to avoid early resizing.

Q5. How does ArrayList grow when it runs out of space?

A: When the array is full, ArrayList computes a new capacity as oldCapacity + (oldCapacity >> 1), which is approximately 1.5x the old capacity. It then calls Arrays.copyOf() to copy all elements into a new, larger array. This is an O(n) operation, but amortized over many adds it results in O(1) amortized cost per add.

// ArrayList growth formula (from OpenJDK source):
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1); // ~1.5x

// Example: 10 -> 15 -> 22 -> 33 -> 49 -> 73 ...
// Arrays.copyOf creates a new array and copies elements
elementData = Arrays.copyOf(elementData, newCapacity);

Q6. What is the difference between ArrayList and LinkedList?

A: This is one of the most common Java interview questions. The key differences span internal structure, performance, and use cases:

AspectArrayListLinkedList
Internal structureObject[] arrayDoubly linked Node objects
Random access get(i)O(1) — direct indexO(n) — must traverse
add at tailO(1) amortizedO(1)
add at headO(n) — shift elementsO(1)
add/remove at indexO(n) — shift elementsO(n) — traverse + O(1) pointer change
Memory overheadLow — only arrayHigher — prev/next/item per node
Cache localityExcellent (contiguous)Poor (scattered heap)
Implements DequeNoYes
Best use caseRead-heavy, random accessFrequent head/tail insert/delete

Q7. What interfaces does LinkedList implement?

A: LinkedList implements List<E>, Deque<E>, Queue<E>, Cloneable, and Serializable. Because it implements Deque, it can be used as a stack (push/pop), queue (offer/poll), or double-ended queue with addFirst/addLast/removeFirst/removeLast operations, all in O(1).

Q8. When would you prefer LinkedList over ArrayList in practice?

A: In practice, ArrayList is almost always preferred due to better cache locality. However, LinkedList may be preferred when: (1) you need very frequent insertions/deletions at the head of the list, (2) you are using it as a queue or deque (though ArrayDeque is usually better), (3) you iterate sequentially and never use random access, (4) you have an unknown/variable number of elements and want to avoid array copying. In reality, ArrayList outperforms LinkedList in most real-world benchmarks due to CPU cache effects.

Q9. What is the time complexity of get(int index) in ArrayList vs LinkedList?

A: ArrayList get(index) is O(1) — it simply accesses elementData[index] directly. LinkedList get(index) is O(n) — it must traverse from the head (or tail, if index > size/2) to reach the element. This is a critical difference in read-heavy use cases.

Q10. What is the time complexity of add(E e) at the end for ArrayList?

A: O(1) amortized. Most add operations simply place the element at index size and increment size — O(1). Occasionally, when the backing array is full, a resize occurs copying all n elements — O(n). Amortized over n insertions, the cost averages to O(1) per insertion using the doubling/1.5x growth strategy.

Q11. What is the time complexity of add(int index, E e) in ArrayList?

A: O(n) in the worst case. ArrayList must shift all elements from index to size-1 one position to the right using System.arraycopy(). If adding at index 0 (the head), all n elements are shifted — O(n). Adding at the tail (same as add(e)) is O(1) amortized.

Q12. What is the time complexity of remove(int index) in ArrayList?

A: O(n). After removing the element at index, all elements from index+1 to size-1 must shift left by one using System.arraycopy(). Removing the last element is O(1) since no shifting is needed.

Q13. What are the time complexities for LinkedList operations?

A: Summary of LinkedList time complexities:

OperationComplexityReason
get(index)O(n)Traverse from head or tail
addFirst / offerFirstO(1)Head pointer update
addLast / offerLastO(1)Tail pointer update
removeFirst / pollFirstO(1)Head pointer update
removeLast / pollLastO(1)Tail pointer update
add(index, e)O(n)Traverse to index + pointer change
contains(o)O(n)Sequential scan
size()O(1)Stored as field

Q14. What is CopyOnWriteArrayList and when should you use it?

A: CopyOnWriteArrayList (in java.util.concurrent) is a thread-safe variant of ArrayList. Every mutating operation (add, remove, set) creates a fresh copy of the underlying array, performs the mutation on the copy, and then atomically replaces the reference. Reads are completely unsynchronized and work on the snapshot. Use it when: reads vastly outnumber writes, you need thread safety without locking, and you can tolerate the cost of array copying on writes. It is ideal for event-listener lists and observer patterns.

import java.util.concurrent.CopyOnWriteArrayList;

CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
list.add("Java");
list.add("Spring");

// Safe concurrent iteration — iterator sees snapshot
for (String s : list) {
    list.add("Go"); // No ConcurrentModificationException!
    System.out.println(s);
}
// "Java", "Spring" printed; "Go" added after snapshot

Q15. What is the difference between Arrays.asList(), List.of(), and new ArrayList()?

A: These three methods create List instances with very different mutability contracts:

MethodStructural Mutationset() allowedAllows nullBacked by array
Arrays.asList()No (UOE)YesYesYes — original array
List.of() (Java 9+)No (UOE)No (UOE)No (NPE)No — internal
new ArrayList()YesYesYesYes — internal copy

Q16. What is List.copyOf() introduced in Java 10?

A: List.copyOf(Collection<? extends E> coll) returns a truly unmodifiable List containing the elements of the given collection, in encounter order. It is a shallow copy — element references are copied, not the objects themselves. It throws NullPointerException if the collection or any element is null. The returned list is unmodifiable (no add/remove/set), similar to List.of(). If the input is already an unmodifiable List created by List.of() or List.copyOf(), the JDK may return the same instance as an optimization.

Q17. What is the subList() method and what is its pitfall?

A: subList(int fromIndex, int toIndex) returns a view of the portion of the list between fromIndex (inclusive) and toIndex (exclusive). It is NOT a copy — it is backed by the original list. Structural modifications to the parent list after obtaining a subList result in a ConcurrentModificationException when the subList is accessed. Modifications through the subList view are reflected in the parent. The common pitfall is forgetting the view relationship and mutating the original list.

List<Integer> list = new ArrayList<>(Arrays.asList(1,2,3,4,5));
List<Integer> sub = list.subList(1, 4); // [2,3,4] — view, not copy

sub.set(0, 99); // modifies list[1] to 99
System.out.println(list); // [1, 99, 3, 4, 5]

list.add(6); // structural change to parent!
System.out.println(sub.get(0)); // ConcurrentModificationException

// Safe: make a copy immediately
List<Integer> copy = new ArrayList<>(list.subList(1, 4));

// Useful pattern: clear a range
list.subList(1, 3).clear(); // removes index 1 and 2 from list

Q18. What is a fail-fast iterator?

A: A fail-fast iterator throws ConcurrentModificationException if the underlying collection is structurally modified after the iterator is created, through any means other than the iterator's own remove() method. It uses an internal modCount counter — the iterator captures modCount at creation and checks it before each next() call. If they differ, it throws CME. This is a best-effort mechanism — it is not guaranteed in all cases (e.g., with concurrent threads without synchronization). Most standard Java collection iterators (ArrayList, HashMap, etc.) are fail-fast.

Q19. How can you avoid ConcurrentModificationException when removing elements from a List during iteration?

A: There are four safe approaches:

List<String> list = new ArrayList<>(Arrays.asList("a","b","c","d"));

// 1. Iterator.remove() — safe
Iterator<String> it = list.iterator();
while (it.hasNext()) {
    if (it.next().equals("b")) it.remove(); // safe
}

// 2. removeIf (Java 8+) — cleanest
list.removeIf(s -> s.equals("c"));

// 3. Iterate a copy, remove from original
for (String s : new ArrayList<>(list)) {
    if (s.equals("d")) list.remove(s);
}

// 4. Collect to remove, then removeAll
List<String> toRemove = list.stream()
    .filter(s -> s.equals("a"))
    .collect(Collectors.toList());
list.removeAll(toRemove);

Q20. What is ListIterator and how does it differ from Iterator?

A: ListIterator<E> extends Iterator<E> and provides bidirectional traversal plus in-place modification. Key additional methods: hasPrevious(), previous(), nextIndex(), previousIndex(), add(E e) (inserts before the cursor), and set(E e) (replaces the last element returned). Iterator can only traverse forward and only remove. ListIterator is available on List implementations (not Set or Queue).

Q21. How does the modCount mechanism work in ArrayList?

A: ArrayList (via AbstractList) maintains an int field modCount that counts structural modifications — operations that change the list's size or otherwise modify it in a way that in-progress iterations could be confused by. Every add, remove, clear, and addAll increments modCount. When you call iterator(), the returned Itr object captures expectedModCount = modCount. On each call to next(), it checks modCount != expectedModCount and throws CME if true.

Q22. What is Vector and how does it differ from ArrayList?

A: Vector is a legacy class from Java 1.0 that is similar to ArrayList — it is also backed by a resizable Object[]. Key differences: (1) Vector is synchronized — all public methods are synchronized, making it thread-safe but slow. (2) Vector doubles its capacity on growth (100% growth factor) vs ArrayList's 1.5x. (3) Vector has legacy methods like addElement(), elementAt(), removeElement() that predate the Collections framework. In modern code, prefer ArrayList for single-threaded use or CopyOnWriteArrayList/synchronized wrappers for concurrent use. Vector is considered obsolete.

Q23. What is Stack in Java and why is it considered obsolete?

A: java.util.Stack extends Vector and provides push/pop/peek/search operations for LIFO behavior. It is considered obsolete because: (1) it inherits all synchronized methods from Vector, adding unnecessary overhead, (2) it inherits problematic List methods like add(0, e) which break LIFO semantics, (3) the class hierarchy is wrong (Stack extending Vector is an IS-A violation). The recommended replacement is ArrayDeque used as a stack (via push/pop/peek), which is faster and not encumbered by synchronization.

Q24. Why is ArrayDeque preferred over Stack?

A: ArrayDeque is preferred because: (1) it is not synchronized — faster in single-threaded contexts, (2) it does not expose List methods that could violate LIFO/FIFO semantics, (3) it is backed by a resizable array, giving excellent cache locality, (4) it is generally faster than LinkedList for stack and queue operations, (5) the Deque interface has clear stack/queue semantics (push/pop/peek for stack; offer/poll/peek for queue). The Java documentation itself recommends Deque over Stack.

Q25. What is Collections.unmodifiableList() and what does it guarantee?

A: Collections.unmodifiableList(List<? extends T> list) returns a view of the given list that throws UnsupportedOperationException on any mutation attempt (add, remove, set, clear, etc.). However, it is only a view wrapper — if the underlying list is mutated directly, those changes are visible through the unmodifiable view. It is NOT truly immutable. For true immutability, use List.of() or List.copyOf() (Java 9/10+).

Q26. What is Collections.synchronizedList() and what caveat must you know?

A: Collections.synchronizedList(List<T> list) wraps a list so that every method call is synchronized on the returned list object, making individual operations thread-safe. The critical caveat: compound operations and iteration are NOT automatically thread-safe. You must manually synchronize on the returned list object when iterating:

List<String> syncList = Collections.synchronizedList(new ArrayList<>());
syncList.add("a"); // thread-safe individually

// WRONG — not thread-safe, iterator is not synchronized
for (String s : syncList) { ... }

// CORRECT — manually synchronize on the list
synchronized (syncList) {
    Iterator<String> it = syncList.iterator();
    while (it.hasNext()) {
        System.out.println(it.next());
    }
}

Q27. What does ensureCapacity() do in ArrayList?

A: ensureCapacity(int minCapacity) pre-allocates internal array capacity to at least minCapacity, if it is currently smaller. This is a performance optimization — if you know you will be adding a large number of elements, calling ensureCapacity before adding avoids multiple intermediate resizes. Example: list.ensureCapacity(10000) before a loop that adds 10,000 elements will allocate one large array upfront rather than resizing ~8 times.

Q28. What does trimToSize() do in ArrayList?

A: trimToSize() trims the capacity of the ArrayList to the current size — it shrinks the backing array to exactly fit the elements. Useful when you have finished adding elements and want to minimize memory usage. If an ArrayList with capacity 100 contains 15 elements, trimToSize() reallocates a 15-element array. This is a tradeoff: reduced memory vs. cost of array reallocation.

Q29. How does the removeIf() method work in ArrayList?

A: removeIf(Predicate<? super E> filter) (Java 8+) removes all elements for which the predicate returns true. The ArrayList implementation uses a single pass, marking elements to remove using a BitSet, then compacting the remaining elements in a second pass — making it more efficient than removing in a loop. It returns true if any elements were removed and increments modCount appropriately.

List<Integer> nums = new ArrayList<>(Arrays.asList(1,2,3,4,5,6,7,8,9,10));
nums.removeIf(n -> n % 2 == 0); // remove even numbers
System.out.println(nums); // [1, 3, 5, 7, 9]

Q30. How does replaceAll() work on a List?

A: replaceAll(UnaryOperator<E> operator) (Java 8+) replaces each element with the result of applying the operator to that element. It is equivalent to a forEach with set() but more concise. It modifies the list in place.

List<String> names = new ArrayList<>(Arrays.asList("alice","bob","carol"));
names.replaceAll(String::toUpperCase);
System.out.println(names); // [ALICE, BOB, CAROL]

List<Integer> nums = new ArrayList<>(Arrays.asList(1,2,3,4,5));
nums.replaceAll(n -> n * n);
System.out.println(nums); // [1, 4, 9, 16, 25]

Q31. How do you sort a List with a Comparator?

A: In Java 8+, use list.sort(Comparator) directly on the list, or Collections.sort(list, comparator). Both use TimSort (O(n log n), stable). You can chain comparators using Comparator.comparing(), thenComparing(), and reversed().

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

// Natural order
names.sort(Comparator.naturalOrder());

// Reverse order
names.sort(Comparator.reverseOrder());

// By length, then alphabetically
names.sort(Comparator.comparingInt(String::length)
           .thenComparing(Comparator.naturalOrder()));

// Complex object sort
List<Employee> emps = getEmployees();
emps.sort(Comparator.comparing(Employee::getDept)
          .thenComparing(Employee::getSalary).reversed());

Q32. How do you perform binary search on a List?

A: Use Collections.binarySearch(List list, T key) or Collections.binarySearch(List list, T key, Comparator c). The list MUST be sorted in ascending order before calling. It returns the index of the element if found, or -(insertion point) - 1 if not found. ArrayList benefits from true O(log n) binary search since it supports random access. LinkedList degrades to O(n) because binary search requires O(log n) random-access calls, each O(n) for LinkedList.

Q33. What are Collections.frequency() and Collections.disjoint()?

A: Collections.frequency(Collection c, Object o) returns the number of elements in c that are equal to o. Collections.disjoint(Collection c1, Collection c2) returns true if the two collections have no elements in common. Both use equals() for comparison.

Q34. What does Collections.nCopies() do?

A: Collections.nCopies(int n, T o) returns an immutable List containing n copies of the specified object. The returned list is serializable and all n elements are the same reference (not deep copies). Useful for initializing a list with a default value or for testing.

List<String> fiveHellos = Collections.nCopies(5, "Hello");
System.out.println(fiveHellos); // [Hello, Hello, Hello, Hello, Hello]

// Create a mutable copy with nCopies
List<Integer> zeros = new ArrayList<>(Collections.nCopies(10, 0));
zeros.set(3, 99); // works because mutable

Q35. What is the difference between List.of() and Collections.unmodifiableList()?

A: List.of() creates a truly immutable list from scratch — the underlying storage cannot be changed. Collections.unmodifiableList() creates a read-only view over an existing list — if the underlying list is modified directly, changes appear through the unmodifiable view. List.of() does not allow null elements; unmodifiableList() allows nulls if the underlying list contains them. List.of() was introduced in Java 9 as the preferred way to create immutable lists.

Q36. How do you safely convert an array to a mutable ArrayList?

A: Arrays.asList(array) is fixed-size. To get a fully mutable ArrayList: new ArrayList<>(Arrays.asList(myArray)). For primitive arrays (int[], double[]), Arrays.asList does NOT work directly — you need to use streams: Arrays.stream(intArray).boxed().collect(Collectors.toList()) or Java 16+ Collectors.toUnmodifiableList().

Q37. What happens when you pass a null to List.of()?

A: List.of(null) throws a NullPointerException immediately at construction time. This is by design — List.of() prohibits null elements to enable better implementations (e.g., internal storage without null checks). If you need null support, use an ArrayList or Arrays.asList().

Q38. What is the iterator() method contract in the Collection interface?

A: The iterator() method returns an Iterator<E> object that provides sequential access to elements. The Iterator contract includes: hasNext() returns true if there are more elements; next() returns the next element and advances the cursor; remove() removes the last element returned by next() from the underlying collection — this is the only safe removal during iteration. Calling remove() without a preceding next(), or calling it twice, throws IllegalStateException.

Q39. What is the enhanced for-loop and what iterator does it use?

A: The enhanced for-loop (for (T t : collection)) is syntactic sugar for using the Iterator returned by collection.iterator(). The compiler desugars it into an iterator-based while loop. Since it uses a standard iterator, it is subject to the same fail-fast behavior — structural modification during enhanced for-loop iteration causes ConcurrentModificationException.

Q40. How does LinkedList implement the Deque interface?

A: LinkedList's doubly-linked structure (each node has prev/next pointers plus a data reference) makes O(1) operations at both ends trivial. The first node (first pointer) serves as the head of the Deque; the last node (last pointer) serves as the tail. Deque methods map directly: addFirst() updates first, addLast() updates last, removeFirst() unlinks first, removeLast() unlinks last — all without traversal, all O(1).

Q41. What is the internal node structure of LinkedList?

A: LinkedList uses a private static nested class:

private static class Node<E> {
    E item;
    Node<E> next;
    Node<E> prev;

    Node(Node<E> prev, E element, Node<E> next) {
        this.item = element;
        this.next = next;
        this.prev = prev;
    }
}

// LinkedList holds:
transient int size = 0;
transient Node<E> first; // head sentinel
transient Node<E> last;  // tail sentinel

Each node stores a reference to its element and references to the previous and next nodes.

Q42. What is the memory overhead of LinkedList vs ArrayList?

A: Each LinkedList node object consumes approximately 24 bytes of overhead on a 64-bit JVM (object header: 16 bytes, plus 3 references at 4–8 bytes each for item/next/prev). For n elements, that is 24n bytes of overhead plus the element references. ArrayList has minimal overhead — just the array object plus potentially unused slots. For small elements or large lists, ArrayList is significantly more memory-efficient. For 1 million Integer elements, LinkedList may use 3-4x more memory than ArrayList.

Q43. Can ArrayList contain duplicate elements?

A: Yes. ArrayList (and LinkedList) allows duplicate elements. The same object reference can appear multiple times, and objects that are equal() can both be present. The List contract explicitly allows duplicates, distinguishing List from Set.

Q44. Can ArrayList contain null elements?

A: Yes. ArrayList allows null elements, and multiple nulls can be stored. LinkedList also allows nulls. Collections.contains(null) and indexOf(null) work with nulls using the null == element comparison in the search loop. Be cautious: methods that call methods on elements (like sort with a natural-order Comparator) will throw NullPointerException if nulls are present.

Q45. What is the difference between remove(int index) and remove(Object o) in ArrayList?

A: remove(int index) removes the element at the specified position and returns it. remove(Object o) removes the first occurrence of the specified element (using equals()) and returns true if found. The ambiguity arises with Integer lists: list.remove(1) calls remove(int index) = remove element at index 1; list.remove(Integer.valueOf(1)) calls remove(Object o) = remove the Integer 1 from the list. This is a common pitfall in interviews and real code.

Q46. How does contains() work in ArrayList?

A: contains(Object o) iterates through the array linearly comparing each element using equals() (or == for null check). It is an O(n) operation. If o is null, it checks for array slots that are null. For frequent membership queries, consider using a HashSet instead of ArrayList for O(1) contains().

Q47. What does Collections.shuffle() do?

A: Collections.shuffle(List list) randomly permutes the list using the default Random source. Collections.shuffle(List list, Random rnd) allows specifying the random source. It uses the Fisher-Yates (Knuth) shuffle algorithm — O(n). Works efficiently on random-access lists (ArrayList). For sequential-access lists (LinkedList), it dumps elements into an array, shuffles, and copies back.

Q48. What is the significance of the Comparable and Comparator interfaces in list sorting?

A: Comparable<T> defines the natural ordering of a class via compareTo(T o). Objects implementing Comparable can be sorted by Collections.sort(list) or list.sort(null). Comparator<T> defines an external ordering strategy via compare(T o1, T o2). Comparator allows multiple sort orders without modifying the class. Java 8 added Comparator factory methods: comparing(), comparingInt(), thenComparing(), reversed(), nullsFirst(), nullsLast().

Q49. What sorting algorithm does Collections.sort() use?

A: Since Java 7, Collections.sort() delegates to List.sort() which uses TimSort — a hybrid stable sorting algorithm derived from Merge Sort and Insertion Sort. TimSort has O(n log n) worst case and O(n) best case (for already sorted data). It is a stable sort, meaning equal elements maintain their relative order. For arrays, Arrays.sort(Object[]) also uses TimSort; Arrays.sort(int[]) uses dual-pivot QuickSort.

Q50. How does ArrayList handle serialization?

A: ArrayList implements Serializable but uses custom serialization via writeObject() and readObject() methods. The backing array elementData is marked transient — meaning default serialization would skip it. The custom writeObject writes only the size and the actual elements (not the full array capacity), avoiding serializing empty trailing slots. readObject reconstructs the array from the deserialized elements. This reduces serialized form size when the array has excess capacity.

Q51. What is a RandomAccess marker interface and why does it matter?

A: java.util.RandomAccess is a marker interface (no methods) that a List implementation uses to signal that it supports efficient random access (O(1) get by index). ArrayList implements RandomAccess; LinkedList does not. Algorithms like Collections.binarySearch() check for RandomAccess to choose between index-based or iterator-based traversal. You can write generic code: if (list instanceof RandomAccess) { use index loop } else { use iterator }.

Q52. What is the addAll() method and how does it perform?

A: addAll(Collection<? extends E> c) appends all elements of c to the end of the list. addAll(int index, Collection<? extends E> c) inserts all elements of c at a specified position. Internally, ArrayList converts c to an array and uses System.arraycopy() for efficient bulk insertion. The list grows if needed. Performance is O(n + k) where n is any needed shift for middle insertion and k is the size of c.

Q53. How do you create an immutable list in Java?

A: Multiple ways in modern Java:

// Java 9+: List.of() — truly immutable, no nulls
List<String> immutable1 = List.of("a", "b", "c");

// Java 10+: List.copyOf() — truly immutable copy
List<String> source = new ArrayList<>(Arrays.asList("x","y","z"));
List<String> immutable2 = List.copyOf(source);

// Collections.unmodifiableList() — unmodifiable VIEW (underlying can change)
List<String> mutable = new ArrayList<>(Arrays.asList("p","q","r"));
List<String> unmod = Collections.unmodifiableList(mutable);

// Google Guava
// List<String> guava = ImmutableList.of("a","b","c");

// Java 8 stream approach
List<String> immutable3 = Stream.of("a","b","c")
    .collect(Collectors.toUnmodifiableList()); // Java 10+

Q54. What is the difference between size() and capacity() in ArrayList?

A: size() returns the number of elements actually stored in the ArrayList. capacity() is NOT a public method — the internal capacity is the length of the backing array (elementData.length). ArrayList does not expose capacity directly (no public capacity() method like C++ vector). You can observe it indirectly or use ensureCapacity() to influence it. Size is always <= capacity.

Q55. What does clear() do in ArrayList?

A: clear() sets all elements in the backing array to null (to allow GC) and sets size to 0. It does NOT shrink the backing array — the capacity remains unchanged. If you want to free the memory, call trimToSize() after clear(), or simply create a new ArrayList. The time complexity is O(n) because each element reference must be nulled out.

Q56. How does indexOf() work differently from contains()?

A: indexOf(Object o) returns the index of the first occurrence of the element, or -1 if not found. lastIndexOf(Object o) returns the last occurrence. contains(Object o) returns a boolean and delegates to indexOf internally. All three are O(n) linear scans.

Q57. How do you iterate a List in reverse order?

A: Three approaches:

List<String> list = Arrays.asList("a","b","c","d");

// 1. Index-based reverse loop
for (int i = list.size() - 1; i >= 0; i--) {
    System.out.println(list.get(i));
}

// 2. ListIterator backward (works for any List)
ListIterator<String> it = list.listIterator(list.size());
while (it.hasPrevious()) {
    System.out.println(it.previous());
}

// 3. Collections.reverse() — modifies list in place
List<String> mutable = new ArrayList<>(list);
Collections.reverse(mutable);

Q58. What is the fail-safe iterator? Do ArrayList or LinkedList have one?

A: A fail-safe iterator works on a snapshot/copy of the collection and does NOT throw ConcurrentModificationException when the original collection is modified. CopyOnWriteArrayList's iterator is fail-safe — it iterates over the snapshot taken at iterator creation time. Standard ArrayList and LinkedList use fail-fast iterators, not fail-safe ones. In the java.util.concurrent package, classes like CopyOnWriteArrayList and ConcurrentHashMap provide fail-safe iterators.

Q59. How does List.sort() differ from Collections.sort()?

A: In Java 8+, List.sort(Comparator) is a default method on the List interface that implementations can override for efficiency. Collections.sort(List, Comparator) delegates to list.sort(comparator) internally. They are functionally equivalent. List.sort() is slightly preferred as it allows implementations to override for performance. Both use TimSort.

Q60. What is the contract of the equals() method for Lists?

A: Two Lists are equal (list1.equals(list2)) if and only if: (1) they are both Lists, (2) they have the same size, and (3) all corresponding pairs of elements are equal (e1.equals(e2) or both null). The order matters — [1,2,3] does NOT equal [3,2,1]. This is defined in the AbstractList.equals() implementation and is the same for ArrayList, LinkedList, and other List implementations.

Q61. What is the hashCode() contract for Lists?

A: The List hashCode is computed as: start with result=1, then for each element e: result = 31 * result + (e==null ? 0 : e.hashCode()). This ensures that two equal Lists have the same hashCode. This is specified in AbstractList.hashCode() and inherited by ArrayList and LinkedList.

Q62. How is memory layout different between ArrayList and LinkedList for iteration performance?

A: ArrayList stores elements in a contiguous Object[] array. Modern CPUs have cache lines of 64 bytes that prefetch adjacent memory. When you iterate ArrayList, sequential elements are loaded into CPU cache together, resulting in excellent spatial locality and very few cache misses. LinkedList nodes are scattered across the heap — each node allocation is at a different memory address. Iteration causes frequent cache misses as each next pointer leads to a different memory location. For iteration-heavy workloads, ArrayList can be 5-10x faster than LinkedList in practice.

Q63. What happens if you call get(-1) on an ArrayList?

A: It throws IndexOutOfBoundsException with a message like "Index: -1, Size: n". ArrayList's rangeCheck method validates that index is in [0, size-1] before accessing the array. Any negative index or index >= size throws this exception.

Q64. Can you use Collections.sort() on an unmodifiable list?

A: No. Collections.sort() calls list.sort() which internally uses list.set() during sorting. An unmodifiable list's set() throws UnsupportedOperationException. You must either sort a mutable copy or use list.stream().sorted().collect(...) to get a new sorted list without modifying the original.

Q65. What is the toArray() method and how do you use it correctly?

A: toArray() returns an Object[] of all elements. toArray(T[] a) returns a T[] — if the provided array is large enough, elements are stored there; otherwise a new array is allocated. The correct generic idiom pre-Java 11: String[] arr = list.toArray(new String[0]). Java 11+: String[] arr = list.toArray(String[]::new). Passing size 0 is idiomatic and actually more efficient in modern JVMs than passing the exact size.

Q66. What is the difference between peek(), poll(), and remove() in a Queue (LinkedList)?

A: When using LinkedList as a Queue: peek() returns the head element without removing it, returns null if empty. poll() returns and removes the head element, returns null if empty. remove() returns and removes the head element, throws NoSuchElementException if empty. The null-returning versions (peek/poll/offer) are preferred for queue usage as they avoid exception handling. Similarly, element() is like peek() but throws on empty.

Q67. What are the push() and pop() methods of LinkedList?

A: When using LinkedList as a Stack (via Deque interface): push(e) is equivalent to addFirst(e) — adds to the head (top of stack). pop() is equivalent to removeFirst() — removes from the head, throwing NoSuchElementException if empty. peek() returns peekFirst() — head without removal. This gives LIFO (Last-In-First-Out) behavior.

Q68. How does Collections.min() and Collections.max() work with Lists?

A: Collections.min(Collection) and Collections.max(Collection) iterate through all elements comparing with the natural ordering (Comparable). They also accept a Comparator overload. They are O(n) operations. They throw NoSuchElementException on empty collections and ClassCastException if elements are not mutually comparable.

Q69. What is Collections.rotate() and when is it useful?

A: Collections.rotate(List list, int distance) rotates the list by distance positions. A positive distance rotates right (toward higher indices); negative rotates left. Example: rotate([1,2,3,4,5], 2) gives [4,5,1,2,3]. Useful for circular buffer operations and certain interview problems. O(n) complexity.

Q70. What is the Spliterator and how does ArrayList use it?

A: Spliterator (Java 8+) is an iterator designed for parallel traversal. ArrayList provides an ArrayListSpliterator with characteristics ORDERED, SIZED, SUBSIZED. It can be split into halves for parallel stream processing. list.spliterator() returns it. Used internally by list.stream() and list.parallelStream(). The spliterator is also fail-fast.

Q71. How does forEach() work on ArrayList vs the enhanced for-loop?

A: list.forEach(Consumer) (Java 8+) is a default method that iterates elements using an index-based loop internally in ArrayList (via the override). The enhanced for-loop uses an iterator. forEach on ArrayList is marginally faster because it avoids the iterator object overhead and does the modCount check once at the end rather than per element. Both are O(n). forEach throws CME at the end if the list was modified; the iterator throws CME on next().

Q72. What is the thread-safety of ArrayList?

A: ArrayList is NOT thread-safe. Concurrent modifications without external synchronization can result in data corruption, incorrect size, ArrayIndexOutOfBoundsException, or infinite loops. Options for thread safety: (1) Collections.synchronizedList() — wraps with per-method synchronization, (2) CopyOnWriteArrayList — copy-on-write for read-heavy concurrent access, (3) external synchronization with synchronized blocks or ReentrantLock, (4) use concurrent data structures like ConcurrentLinkedQueue if queue semantics suffice.

Q73. What is the difference between iterator.remove() and list.remove()?

A: iterator.remove() removes the element last returned by next() from the underlying collection without incrementing modCount in a way that triggers CME on the current iterator — it adjusts the iterator's expectedModCount to match. list.remove() directly removes from the list and increments modCount, making any existing iterators fail-fast. Using iterator.remove() is the only safe way to remove during iteration with a standard iterator.

Q74. How do you copy one List into another?

A: Several approaches:

List<String> source = Arrays.asList("a","b","c");

// 1. Copy constructor — most common
List<String> copy1 = new ArrayList<>(source);

// 2. addAll
List<String> copy2 = new ArrayList<>();
copy2.addAll(source);

// 3. List.copyOf() — immutable
List<String> copy3 = List.copyOf(source);

// 4. Collections.copy() — copies into existing list
List<String> dest = new ArrayList<>(Arrays.asList("x","y","z"));
Collections.copy(dest, source); // dest must be at least as large as source

// 5. Stream
List<String> copy5 = source.stream().collect(Collectors.toList());

Q75. What does Collections.swap() do?

A: Collections.swap(List list, int i, int j) swaps the elements at positions i and j in the list. O(1) for RandomAccess lists (uses get+set), O(n) for sequential-access lists. Useful in sorting algorithm implementations and array manipulation problems.

Q76. How does ArrayList handle the case where initialCapacity is 0?

A: new ArrayList<>(0) initializes elementData to a shared EMPTY_ELEMENTDATA constant (a shared empty Object[] distinct from DEFAULTCAPACITY_EMPTY_ELEMENTDATA). On the first add, it grows to capacity 1 (not 10, unlike the default constructor). Subsequent grows follow the 1.5x formula: 1 -> 2 -> 3 -> 4 -> 6 -> 9... This is different from the default constructor which jumps to 10 on first add.

Q77. What is the relationship between AbstractList, AbstractCollection, and ArrayList?

A: The hierarchy is: Object <- AbstractCollection <- AbstractList <- ArrayList. AbstractCollection provides skeletal implementations of Collection methods (using iterator-based algorithms). AbstractList extends AbstractCollection and adds skeletal List implementations (like indexOf, lastIndexOf, iterator, listIterator, equals, hashCode, subList). ArrayList overrides most methods with optimized array-based implementations. This Skeletal Implementation pattern (from Effective Java) minimizes the effort to implement these interfaces.

Q78. What is the difference between offer() and add() in a Queue?

A: Both add an element to the queue. add(e) throws an IllegalStateException if the element cannot be added (capacity-restricted queues). offer(e) returns false instead of throwing an exception. For LinkedList (which is unbounded), both behave the same way. For bounded queues like ArrayBlockingQueue, the distinction matters. The Deque interface has offerFirst()/offerLast() and addFirst()/addLast() with the same contract.

Q79. How would you find all duplicates in a List?

A: Common approaches:

List<Integer> list = Arrays.asList(1,2,3,2,4,3,5,1);

// Stream approach — O(n)
Set<Integer> seen = new HashSet<>();
Set<Integer> duplicates = list.stream()
    .filter(e -> !seen.add(e))
    .collect(Collectors.toSet());
System.out.println(duplicates); // [1, 2, 3]

// Using frequency
Set<Integer> dupes = list.stream()
    .filter(e -> Collections.frequency(list, e) > 1)
    .collect(Collectors.toSet()); // O(n^2) — less efficient

Q80. What is the Collections.fill() method?

A: Collections.fill(List<? super T> list, T obj) replaces all elements of the specified list with the specified element. The list size remains unchanged. It is O(n) and requires a mutable list. Example: Collections.fill(list, "default") sets every element to "default".

Q81. What is the difference between List and AbstractList?

A: List is the interface defining the contract. AbstractList is an abstract class that provides a skeletal implementation of the List interface to minimize the effort required to implement it. To create a custom List, extending AbstractList requires implementing only get(int index) and size() for an unmodifiable list, plus set(int, E) for a modifiable one. AbstractList provides implementations of iterator(), listIterator(), indexOf(), lastIndexOf(), equals(), hashCode(), subList() etc. based on get() and size().

Q82. What is a structural modification in the context of ArrayList?

A: A structural modification is any operation that changes the size of the list or modifies it in a way that in-progress iterations could produce incorrect results. Examples: add(), addAll(), remove(int), remove(Object), clear(), and addAll(). Non-structural modifications include: set() (replacing an element without changing size). The modCount counter tracks structural modifications for fail-fast iterators.

Q83. How do you convert a List to a Set and back?

A: Converting List to Set removes duplicates; converting back to List loses Set's ordering (except TreeSet/LinkedHashSet):

List<Integer> list = Arrays.asList(1,2,3,2,4,1);

// List -> Set (removes duplicates)
Set<Integer> set = new HashSet<>(list);           // unordered
Set<Integer> linkedSet = new LinkedHashSet<>(list); // insertion order
Set<Integer> treeSet = new TreeSet<>(list);         // sorted

// Set -> List
List<Integer> back = new ArrayList<>(set);

// Using streams
Set<Integer> set2 = list.stream().collect(Collectors.toSet());
List<Integer> distinct = list.stream().distinct().collect(Collectors.toList());

Q84. What is the difference between Collections.singletonList() and List.of() with one element?

A: Both return an immutable list with a single element. Collections.singletonList(e) is available since Java 1.3 and allows a null element. List.of(e) is Java 9+ and rejects null with NPE. Both throw UnsupportedOperationException on mutation attempts. Collections.singletonList() is still useful when null compatibility is needed.

Q85. How do you partition a List into sublists of a given size?

A: Java doesn't have a built-in partition method, but Guava provides Lists.partition(list, size). In plain Java, use subList in a loop:

// Plain Java partition
public static <T> List<List<T>> partition(List<T> list, int size) {
    List<List<T>> partitions = new ArrayList<>();
    for (int i = 0; i < list.size(); i += size) {
        partitions.add(new ArrayList<>(
            list.subList(i, Math.min(i + size, list.size()))
        ));
    }
    return partitions;
}

// Stream approach (Java 9+ with takeWhile/dropWhile or IntStream)
List<Integer> data = IntStream.rangeClosed(1, 10)
    .boxed().collect(Collectors.toList());

int batchSize = 3;
List<List<Integer>> batches = IntStream
    .iterate(0, i -> i < data.size(), i -> i + batchSize)
    .mapToObj(i -> data.subList(i, Math.min(i + batchSize, data.size())))
    .collect(Collectors.toList());

Q86. What is the difference between iterator() and listIterator() starting at a given index?

A: iterator() always starts at the beginning of the list. listIterator(int index) positions the cursor at the specified index — the next call to next() returns the element at that index, and previous() returns the element at index-1. This allows efficient traversal from any point in a List.

Q87. What is the clone() method behavior in ArrayList?

A: ArrayList implements Cloneable and overrides clone() to return a shallow copy — a new ArrayList containing the same element references. The backing array is copied (new Object[]), but the elements themselves are not deeply copied. Changes to the clone's structure (add/remove) do not affect the original, but mutations to shared element objects are visible in both. You should prefer the copy constructor new ArrayList<>(original) over clone() as it is clearer and more idiomatic.

Q88. How does ArrayList handle primitive types?

A: ArrayList cannot store primitives directly — Java generics are limited to reference types. Primitives (int, double, etc.) are auto-boxed to their wrapper types (Integer, Double, etc.) when stored in ArrayList. This incurs boxing/unboxing overhead and extra memory per element. For primitive-heavy workloads, consider int[] arrays or specialized libraries like Eclipse Collections or Trove's TIntArrayList. Java may eventually provide primitive generics via Project Valhalla (JEP 401+).

Q89. What is the difference between List.of() in Java 9 and Set.of() / Map.of()?

A: All three follow the same design: immutable, no nulls, UnsupportedOperationException on mutation. The key difference: List.of() preserves order and allows duplicates. Set.of() and Map.of() reject duplicate keys/values at construction time with an IllegalArgumentException. List.of() iteration order is always the insertion order; Set.of() and Map.of() have unspecified iteration order (may vary between JVM runs).

Q90. How does AbstractSequentialList differ from AbstractList?

A: AbstractSequentialList is a middle-level abstract class that provides a skeletal implementation of List for sequential-access lists (where random access is expensive). It implements get(int), set(int, E), add(int, E), and remove(int) in terms of the list's listIterator — a single required implementation. LinkedList extends AbstractSequentialList. AbstractList is designed for random-access lists and requires implementing get(int) and size(). If you are building a linked/sequential list, extend AbstractSequentialList; for array-backed, extend AbstractList.

Q91. What ConcurrentModificationException issues arise with subList?

A: A subList view maintains a reference to the parent list's modCount. If the parent is structurally modified (add/remove outside the subList), accessing the subList afterwards throws ConcurrentModificationException. Additionally, if you serialize a list and deserialize it, any sublists become stale. Best practice: make a defensive copy new ArrayList<>(list.subList(from, to)) if the subList needs to outlive the parent's modification scope.

Q92. How does Collections.checkedList() work?

A: Collections.checkedList(List<E> list, Class<E> type) returns a dynamically type-safe view of the list. Any attempt to insert an element that is not an instance of type throws ClassCastException immediately, even in pre-generics code or when using raw types. It is a runtime safety wrapper to catch heap pollution errors early. Example: List<String> safe = Collections.checkedList(new ArrayList<>(), String.class).

Q93. What is the difference between CopyOnWriteArrayList.iterator() and ArrayList.iterator()?

A: ArrayList's iterator is fail-fast — it detects concurrent structural modification and throws CME. CopyOnWriteArrayList's iterator works on the snapshot of the array at the time the iterator was created. The iterator never reflects subsequent adds/removes to the list. The iterator does NOT support the remove() operation — calling it throws UnsupportedOperationException. This snapshot semantics means iterators are safe to use concurrently without synchronization.

Q94. What is the recommended way to use LinkedList as a Queue vs ArrayDeque?

A: Both LinkedList and ArrayDeque implement Deque and Queue. However, ArrayDeque is the recommended choice for most queue/deque uses because: (1) no node allocation overhead per element, (2) better cache locality (circular array), (3) no synchronization overhead, (4) faster in practice for both head/tail operations. LinkedList is only preferred if: you specifically need a List+Queue combination, or you need null elements in the queue (ArrayDeque does not allow null). The Java documentation recommends ArrayDeque for stack/queue use cases.

Q95. How do you check if two Lists have the same elements regardless of order?

A: Sort both lists and compare, or use frequency maps:

List<Integer> a = Arrays.asList(3,1,2,1);
List<Integer> b = Arrays.asList(1,3,1,2);

// Approach 1: Sort and compare
List<Integer> sortedA = new ArrayList<>(a);
List<Integer> sortedB = new ArrayList<>(b);
Collections.sort(sortedA);
Collections.sort(sortedB);
boolean equal1 = sortedA.equals(sortedB); // true

// Approach 2: Frequency map (handles duplicates correctly)
Map<Integer, Long> freqA = a.stream()
    .collect(Collectors.groupingBy(e -> e, Collectors.counting()));
Map<Integer, Long> freqB = b.stream()
    .collect(Collectors.groupingBy(e -> e, Collectors.counting()));
boolean equal2 = freqA.equals(freqB); // true

Q96. What is the behavior of Arrays.asList() when the input array is modified?

A: Arrays.asList(T... a) returns a fixed-size List that is backed by the original array. If you modify the array after calling asList(), the changes are reflected in the List, and vice versa (set() on the List updates the array). The list and array share the same storage. This is a common pitfall — to avoid it, wrap with new ArrayList<>(Arrays.asList(array)).

String[] arr = {"a","b","c"};
List<String> list = Arrays.asList(arr);

arr[0] = "MODIFIED";
System.out.println(list.get(0)); // MODIFIED — shared array!

list.set(1, "CHANGED");
System.out.println(arr[1]); // CHANGED — reflected in array!

// list.add("d"); // UnsupportedOperationException — fixed size

Q97. What is the difference between List.of() iteration order in Java 9+ and HashMap order?

A: List.of() always preserves insertion order — iteration order is always the same as the order elements were provided. Set.of() and Map.of() have intentionally randomized iteration order (varies between JVM runs) to discourage dependence on order. ArrayList and LinkedList always maintain insertion order. TreeSet/TreeMap maintain sorted order. LinkedHashSet/LinkedHashMap maintain insertion order.

Q98. How do you implement a thread-safe list with ReentrantReadWriteLock?

A: Using ReadWriteLock allows concurrent reads with exclusive writes — better throughput than full synchronization for read-heavy workloads:

import java.util.concurrent.locks.*;

public class ThreadSafeList<E> {
    private final List<E> list = new ArrayList<>();
    private final ReadWriteLock lock = new ReentrantReadWriteLock();
    private final Lock readLock = lock.readLock();
    private final Lock writeLock = lock.writeLock();

    public void add(E e) {
        writeLock.lock();
        try { list.add(e); }
        finally { writeLock.unlock(); }
    }

    public E get(int index) {
        readLock.lock();
        try { return list.get(index); }
        finally { readLock.unlock(); }
    }

    public int size() {
        readLock.lock();
        try { return list.size(); }
        finally { readLock.unlock(); }
    }
}

Q99. What are the Java 21 / Java 22 improvements relevant to Collections?

A: Recent Java improvements affecting collections: (1) Sequenced Collections (JEP 431, Java 21) — adds SequencedCollection, SequencedSet, SequencedMap interfaces to the hierarchy, providing first/last element access and reversed() views consistently across List, Deque, LinkedHashSet, LinkedHashMap, SortedSet/Map. ArrayList now has getFirst() and getLast() via SequencedCollection. (2) Virtual threads (Project Loom, Java 21) change concurrency considerations — synchronized collections are less problematic when threads are cheap. (3) Record patterns and switch expressions improve working with collection data. (4) Stream API continues to improve with gather() in Java 22 (JEP 461 preview).

Q100. What are SequencedCollection methods added in Java 21?

A: Java 21 introduced the SequencedCollection interface (JEP 431) providing: getFirst(), getLast(), addFirst(e), addLast(e), removeFirst(), removeLast(), and reversed() — returns a reversed-order view. ArrayList, LinkedList, Vector now implement SequencedCollection via List. This eliminates the need to use index arithmetic (get(size()-1)) for last-element access and provides a consistent API across List and Deque.

// Java 21+ SequencedCollection examples
List<String> list = new ArrayList<>(Arrays.asList("a","b","c"));

String first = list.getFirst();  // "a" — no more list.get(0)
String last  = list.getLast();   // "c" — no more list.get(list.size()-1)

list.addFirst("z");  // ["z","a","b","c"]
list.addLast("x");   // ["z","a","b","c","x"]

String removed = list.removeFirst(); // "z"
List<String> rev = list.reversed();  // reversed view ["x","c","b","a"]

Q101. What is the difference between ConcurrentModificationException and UnsupportedOperationException?

A: ConcurrentModificationException (CME) is thrown when a fail-fast iterator detects that the underlying collection was structurally modified during iteration. It signals concurrent access or modification bugs during traversal. UnsupportedOperationException (UOE) is thrown when you attempt a mutating operation on a collection that does not support it — such as calling add() on a List.of() or Arrays.asList() result. CME is about timing; UOE is about capability.

Q102. How does List.of() differ from Collections.emptyList()?

A: Collections.emptyList() returns a single shared immutable empty list — the same instance is returned every call, no allocation. List.of() with no arguments also returns an empty immutable list, and modern JVM implementations similarly return a shared instance. Both are immutable and throw UOE on mutation. Collections.emptyList() has been available since Java 1.5; List.of() since Java 9. Prefer List.of() in modern code for stylistic consistency.

Q103. How do you implement a bounded list in Java (max capacity)?

A: No standard bounded List exists in the JDK. You can implement one by extending ArrayList and overriding add/addAll:

public class BoundedArrayList<E> extends ArrayList<E> {
    private final int maxSize;

    public BoundedArrayList(int maxSize) {
        super(maxSize);
        this.maxSize = maxSize;
    }

    @Override
    public boolean add(E e) {
        if (size() >= maxSize)
            throw new IllegalStateException("List is full: max " + maxSize);
        return super.add(e);
    }

    @Override
    public void add(int index, E e) {
        if (size() >= maxSize)
            throw new IllegalStateException("List is full: max " + maxSize);
        super.add(index, e);
    }
}

Q104. What happens to a ListIterator's state after add() is called?

A: When you call listIterator.add(e), the element is inserted immediately before the implicit cursor position — between the last element returned by next() and the next element. After the add, the cursor advances: nextIndex() increases by 1, and a subsequent call to previous() returns the just-added element. A subsequent call to next() is unaffected (returns what it would have without the add). Calling set() after add() throws IllegalStateException.

Q105. What is the performance implication of using LinkedList when you only need Queue semantics?

A: Using LinkedList as a Queue incurs per-element node allocation overhead — each enqueue allocates a new Node object (24+ bytes) on the heap, increasing GC pressure. ArrayDeque stores elements in a resizable array with no per-element object allocation, resulting in far less GC pressure and better cache performance. For high-throughput queuing (millions of operations), the difference is significant. For concurrent queuing, prefer LinkedBlockingQueue or ArrayBlockingQueue depending on bounded/unbounded needs.

Q106. How does Collections.reverseOrder() work with sorted lists?

A: Collections.reverseOrder() returns a Comparator that imposes the reverse of the natural ordering. Collections.reverseOrder(Comparator c) returns a Comparator that imposes the reverse of the given Comparator. Useful with sort methods: list.sort(Collections.reverseOrder()) sorts in descending order. Equivalent to Comparator.reverseOrder() in Java 8+.

Q107. What is the significance of the transient keyword in ArrayList's fields?

A: In ArrayList, transient Object[] elementData and transient int size (size is NOT transient, only elementData is) are marked transient. The elementData array is transient because ArrayList provides custom writeObject/readObject serialization that serializes only the actual elements (indices 0 to size-1), not the full backing array with potentially empty trailing slots. This produces a more compact serialized form. If elementData were not transient, default serialization would write the entire array including unused capacity slots.

Q108. What is a WeakReference-based list and when is it used?

A: A WeakReference-based list stores WeakReference wrappers around elements. When an element has no strong references elsewhere, the GC can collect it, and the WeakReference returns null. This pattern is used for listener/observer lists where you want listeners to be automatically removed when they are GC'd (no memory leaks from forgotten listeners). You must periodically clean up null references. Example frameworks: Spring's AbstractApplicationContext uses weak references for some listener management.

Q109. What is the relationship between Deque and Stack operations in LinkedList?

A: LinkedList's Deque methods map to Stack semantics as follows: push(e) = addFirst(e), pop() = removeFirst(), peek() = peekFirst(). LIFO order means the most recently pushed element is returned first. The Deque interface effectively supersedes the legacy Stack class for stack use cases, providing a richer API and avoiding the synchronized overhead of Stack/Vector.

Q110. Summarize all key List interview topics: what areas do interviewers test most?

A: Interviewers at top companies typically test these areas:

  • Internals: ArrayList growth factor (1.5x), default capacity (10), lazy initialization, elementData[]
  • Complexity: O(1) get for ArrayList, O(n) for LinkedList; O(1) amortized add-at-end for ArrayList; O(1) add/remove at head/tail for LinkedList
  • Mutability: Arrays.asList() vs List.of() vs new ArrayList() — know the exact mutation rules
  • Concurrency: CopyOnWriteArrayList (fail-safe, copy-on-write), synchronizedList (caveat: iterate with lock), Vector (legacy, deprecated)
  • Iteration: fail-fast modCount, CME causes and fixes, removeIf, ListIterator
  • subList pitfall: view vs copy, CME on parent modification
  • Sort: TimSort, Comparator chaining, stable sort guarantee
  • Utilities: Collections.sort/shuffle/reverse/fill/nCopies/frequency/binarySearch
  • Java 21: SequencedCollection, getFirst/getLast/reversed()
  • Design: When to use ArrayList vs LinkedList vs ArrayDeque vs CopyOnWriteArrayList
No comments
Leave a Comment