Collections and Concurrency Interview
Interview questions on Java Collections Framework and concurrency — among the most frequently tested topics in Java interviews.
Collections Framework
Q: ArrayList vs LinkedList?
| ArrayList | LinkedList | |
|---|---|---|
| Internal | Dynamic array | Doubly linked list |
| Random access | O(1) | O(n) |
| Insert/delete at end | O(1) amortized | O(1) |
| Insert/delete at middle | O(n) | O(1) with iterator |
| Memory | Less overhead | More (node pointers) |
| Best for | Frequent reads | Frequent insert/delete |
Q: HashMap internal working?
put(key, value):
1. hash = key.hashCode() ^ (hash >>> 16)
2. index = hash & (capacity - 1)
3. If bucket empty → insert
4. If bucket occupied → check equals()
- Same key → replace value
- Different key → add to linked list / tree (if > 8 nodes)
5. If size > threshold → resize (double capacity)
Since Java 8: buckets with >8 nodes convert to red-black tree (O(log n) worst case).
Q: HashMap vs ConcurrentHashMap?
| HashMap | ConcurrentHashMap | |
|---|---|---|
| Thread-safe | No | Yes |
| Null keys/values | Allowed | Not allowed |
| Iteration | Fail-fast | Weakly consistent |
| Locking | None | Segment/CAS-based |
| Performance | Fastest (single thread) | Good under contention |
Q: fail-fast vs fail-safe iterators?
// fail-fast (ArrayList, HashMap) — throws ConcurrentModificationException
List<String> list = new ArrayList<>(List.of("a", "b", "c"));
for (String s : list) {
list.remove(s); // ConcurrentModificationException!
}
// fail-safe (CopyOnWriteArrayList, ConcurrentHashMap) — iterates snapshot
List<String> safe = new CopyOnWriteArrayList<>(List.of("a", "b", "c"));
for (String s : safe) {
safe.remove(s); // OK — operates on copy
}
Concurrency
Q: Thread vs Runnable vs Callable?
| Thread | Runnable | Callable | |
|---|---|---|---|
| Return value | No | No | Yes (V) |
| Exception | Unchecked only | Unchecked only | Checked allowed |
| Reuse | No (1:1) | Yes (ExecutorService) | Yes |
| Usage | Avoid extending Thread | Preferred | When return value needed |
ExecutorService pool = Executors.newFixedThreadPool(4);
Future<Integer> future = pool.submit(() -> computeResult());
Integer result = future.get(5, TimeUnit.SECONDS);
pool.shutdown();
Q: synchronized vs ReentrantLock?
| synchronized | ReentrantLock | |
|---|---|---|
| Syntax | Built-in keyword | Explicit lock/unlock |
| Release | Automatic | Manual (must unlock in finally) |
| Fairness | Non-fair default | Configurable fair/non-fair |
| Features | Basic | tryLock, timeout, conditions |
| Performance | Optimized (biased locking) | Slightly more overhead |
Q: volatile keyword?
Guarantees visibility (not atomicity):
private volatile boolean running = true;
// Thread 1
running = false; // immediately visible to Thread 2
// Thread 2
while (running) { /* work */ } // sees false, exits loop
Use for: flags, single-writer scenarios. For compound operations, use AtomicInteger or synchronized.
Q: Explain thread pool parameters.
ThreadPoolExecutor executor = new ThreadPoolExecutor(
4, // corePoolSize
8, // maximumPoolSize
60, TimeUnit.SECONDS, // keepAliveTime
new ArrayBlockingQueue<>(100), // workQueue
new ThreadPoolExecutor.CallerRunsPolicy() // rejection policy
);
Execution order: core threads → queue → extra threads → rejection policy.
Q: Deadlock — how to detect and prevent?
Detection: thread dump (jstack) shows “Found one Java-level deadlock”.
Prevention:
- Lock ordering — always acquire locks in same order
- Lock timeout —
tryLock(timeout) - Avoid nested locks
- Use concurrent collections instead of manual locking
Coding Questions
Q: Implement a thread-safe singleton.
public class Singleton {
private static volatile Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
Or use enum (best approach):
public enum Singleton {
INSTANCE;
public void doWork() { /* ... */ }
}
Q: Print numbers 1-100 alternately with two threads.
Object lock = new Object();
IntSupplier printer = () -> {
for (int i = 1; i <= 100; i += 2) {
synchronized (lock) {
System.out.println(Thread.currentThread().getName() + ": " + i);
lock.notify();
try { lock.wait(); } catch (InterruptedException e) { break; }
}
}
};
new Thread(printer::getAsInt, "Odd").start();
new Thread(() -> { /* even numbers */ }, "Even").start();