Sitemap
Anuj Singh

Anuj Singh

Backend Developer at TCS

Java Collections — Interview Questions & Answers

6 min readAug 15, 2026

--

Press enter or click to view image in full size

Q1. What is the Internal Working of ArrayList?

ArrayList is a resizable array implementation of the List interface.

Internally, it uses an array to store elements:

ArrayList
↓
Object[]
[10] [20] [30] [40] [ ] [ ] ...

When the array becomes full, ArrayList creates a larger array and copies the existing elements into it.
Important points:

  • Allows duplicate elements.
  • Maintains insertion order.
  • Allows null.
  • Provides fast random access using index: O(1).
  • Adding at the end is usually O(1) amortized.
  • Inserting/removing from the middle is O(n) because elements need to be shifted.
  • It is not thread-safe.

Example:

List<Integer> list = new ArrayList<>();

list.add(10);
list.add(20);
list.add(30);

Q2. What is the Internal Working of HashMap?

HashMap stores data as key-value pairs.

Map<String, Integer> map = new HashMap<>();
map.put("Java", 100);

Internally, it uses an array of buckets. When we insert a key, its hashCode() is used to calculate a bucket location.

Conceptually:

Key
↓
hashCode()
↓
Hash calculation
↓
Bucket
↓
Key-Value Entry

If multiple keys map to the same bucket, a collision occurs. Java handles collisions using linked structures, and in modern Java, a heavily-collided bucket can be converted into a Red-Black Tree.

When retrieving:

map.get("Java");

HashMap calculates the hash again, finds the bucket, and compares keys using equals() to locate the correct entry.

Average complexity:

put() → O(1)
get() → O(1)
remove() → O(1)

These are average cases; collisions can affect performance.

Important: HashMap allows one null key and multiple null values and is not thread-safe.

Q3. ConcurrentHashMap vs Hashtable

Both are thread-safe maps, but their concurrency mechanisms are different.

Hashtable

Hashtable is a legacy collection. Its main methods are synchronized, meaning access is heavily serialized.

Hashtable<Integer, String> table = new Hashtable<>();

ConcurrentHashMap

ConcurrentHashMap was designed for high-concurrency applications

ConcurrentHashMap<Integer, String> map =
new ConcurrentHashMap<>();

It allows multiple threads to operate on different parts of the map concurrently and uses modern synchronization/atomic mechanisms internally.

| Hashtable                         | ConcurrentHashMap                  |
| --------------------------------- | ---------------------------------- |
| Legacy | Modern |
| Synchronized methods | Fine-grained concurrency |
| Lower concurrency | Higher concurrency |
| Does not allow `null` key/value | Does not allow `null` key/value |
| Generally slower under contention | Better for concurrent applications |

Q4. Vector vs ArrayList

Don’t choose Vector simply because it is synchronized. For concurrent use, choose a collection based on the actual concurrency requirements.

Both are dynamic arrays, but they differ mainly in thread safety.

Vector

Vector is a legacy collection whose major methods are synchronized.

ArrayList

ArrayList is not synchronized and is therefore generally faster in single-threaded scenarios.

| Vector                                | ArrayList        |
| ------------------------------------- | ---------------- |
| Legacy | Modern |
| Synchronized | Not synchronized |
| Thread-safe for individual operations | Not thread-safe |
| Generally slower | Generally faster |
| Dynamic array | Dynamic array |

Q5. LinkedList vs ArrayList

Don’t assume LinkedList is automatically faster for insertions. If you first need to traverse to the insertion position, that traversal itself can be O(n). In most everyday application code, ArrayList is the better default.

Both implementList, but their internal structures are different.

ArrayList

Uses a dynamic array.

[10] [20] [30] [40]

Provides fast index-based access:

list.get(2);

Time complexity: O(1).

LinkedList

Uses a doubly linked list.

10 ↔ 20 ↔ 30 ↔ 40

Accessing an element by index requires traversal, so:

list.get(30);

is O(n).

| ArrayList                     | LinkedList                                     |
| ----------------------------- | ---------------------------------------------- |
| Dynamic array | Doubly linked list |
| `get()` → O(1) | `get()` → O(n) |
| Better cache locality | More pointer overhead |
| Usually better default choice | Useful for specific insertion/removal patterns |

Q6. TreeMap vs HashMap

Use HashMap when you primarily need fast lookup; use TreeMap when you need keys maintained in sorted order or need navigational operations.

HashMap

Uses hashing and provides average O(1) lookup.

Map<Integer, String> map = new HashMap<>();

It does not maintain sorted key order.

TreeMap

Uses a Red-Black Tree and keeps keys sorted.

Map<Integer, String> map = new TreeMap<>();

Operations such as put(), get(), and remove() generally take O(log n).

| HashMap             | TreeMap                                               |
| ------------------- | ----------------------------------------------------- |
| Hash table | Red-Black Tree |
| Average O(1) | O(log n) |
| No sorted order | Sorted by keys |
| Allows one null key | Null keys generally not allowed with natural ordering |
| Faster lookup | Useful when sorted order is required |

Q7. What are Immutable Classes?

An immutable object cannot be modified after it is created.

Get Anuj Singh’s stories in your inbox

Join Medium for free to get updates from this writer.

The most common example is String.

String name = "Java";

name.concat(" Programming");

System.out.println(name); // Java

concat() does not modify the original String; it creates a new object.

How to create an immutable class?

Typically:

  1. Make the class final.
  2. Make fields private and final.
  3. Initialize fields through the constructor.
  4. Don’t provide setters.
  5. Don’t expose mutable internal objects directly.
  6. Return defensive copies when necessary.

Example:

final class Employee {
private final String name;

Employee(String name) {
this.name = name;
}

public String getName() {
return name;
}
}

Q8. Shallow Copy vs Deep Copy

Shallow Copy

A shallow copy creates a new outer object but references the same nested objects.

Original ──→ Address
Copy ───┘

So modifying the nested object through one copy can affect the other.

Deep Copy

A deep copy creates a new outer object and copies the nested objects as well.

Original → Address A
Copy → Address B

Changes to one object’s nested state don’t affect the other.

Example:

Employee e1 = ...;
Employee e2 = shallowCopy(e1);

Both may point to the same Address.

With deep copying:

Employee e2 = deepCopy(e1);

e2 gets its own Address.

Q9. Comparator vs Comparable

Use Comparable when the class has a clear natural order; use Comparator when sorting requirements can vary.

Both are used for sorting objects.

Comparable

Used when a class defines its natural ordering.

class Employee implements Comparable<Employee> {

private int age;

@Override
public int compareTo(Employee other) {
return Integer.compare(this.age, other.age);
}
}

Then:

Collections.sort(employees);

Comparator

Used when we want to define sorting outside the class or need multiple sorting strategies.

employees.sort(
Comparator.comparing(Employee::getName)
);
| Comparable                   | Comparator                           |
| ---------------------------- | ------------------------------------ |
| `compareTo()` | `compare()` |
| Defines natural ordering | Defines custom ordering |
| Implemented by the class | Separate object/interface |
| Usually one natural ordering | Multiple sorting strategies possible |

Q10. What is CopyOnWriteArrayList?

CopyOnWriteArrayList is a thread-safe List implementation designed for scenarios with many reads and very few writes.

When a modification occurs, it creates a new copy of the underlying array.

Before write:

Readers → [A B C]

Write

After write:

Readers → [A B C]
Writer → [A B C D]

Readers can continue reading without being blocked by the writer.

Advantages:

  • Thread-safe
  • Excellent for read-heavy workloads
  • Iterators don’t throw ConcurrentModificationException due to concurrent modifications

Disadvantage:

  • Every write creates a new array, making writes expensive.
  • Can consume significant memory for large lists.

Typical use cases include configuration data or listener lists that are read frequently but modified rarely.

Q11. What is WeakHashMap and What are References?

WeakHashMap is useful when you don't want the map itself to prevent its keys from being garbage collected.

WeakHashMap stores keys using weak references.

If a key is no longer strongly referenced elsewhere, the Garbage Collector can remove that key from the map.

Map<Key, Value> map = new WeakHashMap<>();

Conceptually:

Strong Reference
↓
Key
↓
WeakHashMap

If the strong reference disappears:

Strong Reference → removed
↓
Key
↓
GC can collect it
↓
WeakHashMap entry disappears

This can be useful for certain caches or metadata where entries should not keep keys alive.

Reference types

Java provides different reference strengths:

  • Strong Reference — normal reference; object remains reachable.
  • Soft Reference — may be collected when memory is needed.
  • Weak Reference — can be collected when no strong references remain.
  • Phantom Reference — used for advanced lifecycle/cleanup mechanisms.

Q12. What is ConcurrentModificationException?

ConcurrentModificationException generally occurs when a collection is structurally modified while it is being iterated, except through the iterator's own supported modification operation.

Example:

List<Integer> numbers =
new ArrayList<>(Arrays.asList(1, 2, 3));

for (Integer n : numbers) {
if (n == 2) {
numbers.remove(n);
}
}

This can throw:

ConcurrentModificationException

The common reason is that the collection detects that it has been structurally modified after the iterator was created.

Correct approach

Use the iterator:

Iterator<Integer> iterator = numbers.iterator();

while (iterator.hasNext()) {
if (iterator.next() == 2) {
iterator.remove();
}
}

Or use appropriate collection APIs:

numbers.removeIf(n -> n == 2);

Important: The name can be misleading. ConcurrentModificationException does not necessarily mean multiple threads are involved. It can happen in a single thread when a collection is modified during iteration.

Also, this behavior is generally fail-fast, not a synchronization guarantee. It is intended to detect bugs early, not to provide thread safety.

These are some of the most frequently asked Java Collections interview questions. If you liked this article, do give it a 👏 Clap!

--

--