Java Collections Framework
Question
Explain Java's Collections Framework. What are the main interfaces and their implementations? How do you choose the right collection type for different use cases? What are the best practices for working with collections?
Answer
The Java Collections Framework provides a unified architecture for representing and manipulating collections of objects. It includes interfaces, implementations, and algorithms for working with collections of data.
Main Interfaces
-
Collection Interface
public class CollectionBasics { public static void collectionOperations() { // List - Ordered collection List<String> list = new ArrayList<>(); list.add("First"); list.add("Second"); list.add(0, "Zero"); // Insert at specific position // Set - Unique elements Set<Integer> set = new HashSet<>(); set.add(1); set.add(1); // Duplicate ignored // Queue - FIFO/LIFO Queue<String> queue = new LinkedList<>(); queue.offer("First"); queue.offer("Second"); String first = queue.poll(); // Deque - Double-ended queue Deque<Integer> deque = new ArrayDeque<>(); deque.addFirst(1); deque.addLast(2); } }
-
Map Interface
public class MapBasics { public static void mapOperations() { // HashMap - General purpose map Map<String, Integer> hashMap = new HashMap<>(); hashMap.put("One", 1); hashMap.put("Two", 2); // TreeMap - Sorted map Map<String, Integer> treeMap = new TreeMap<>(); treeMap.put("Zebra", 1); treeMap.put("Apple", 2); // Automatically sorted // LinkedHashMap - Insertion order Map<String, Integer> linkedMap = new LinkedHashMap<>(); linkedMap.put("First", 1); linkedMap.put("Second", 2); // Maintains insertion order } }
Collection Implementations
-
List Implementations
public class ListImplementations { public static void listTypes() { // ArrayList - Dynamic array List<String> arrayList = new ArrayList<>(); arrayList.add("Item"); // Fast random access // LinkedList - Doubly linked list List<String> linkedList = new LinkedList<>(); linkedList.add("Item"); // Fast insertion/deletion // Vector - Thread-safe ArrayList List<String> vector = new Vector<>(); vector.add("Item"); // Synchronized // Stack - LIFO Stack<String> stack = new Stack<>(); stack.push("Item"); String popped = stack.pop(); } }
-
Set Implementations
public class SetImplementations { public static void setTypes() { // HashSet - Hash table Set<String> hashSet = new HashSet<>(); hashSet.add("Item"); // Fast add/remove // TreeSet - Sorted set Set<String> treeSet = new TreeSet<>(); treeSet.add("Item"); // Maintains order // LinkedHashSet - Insertion order Set<String> linkedSet = new LinkedHashSet<>(); linkedSet.add("Item"); // Maintains insertion order } }
Collection Operations
-
Basic Operations
public class CollectionOperations { public static void operations() { List<String> list = new ArrayList<>(); // Add elements list.add("Item"); list.addAll(Arrays.asList("One", "Two")); // Remove elements list.remove("Item"); list.removeAll(Arrays.asList("One", "Two")); // Check elements boolean contains = list.contains("Item"); boolean isEmpty = list.isEmpty(); int size = list.size(); // Clear collection list.clear(); } }
-
Bulk Operations
public class BulkOperations { public static void bulkOperations() { Set<Integer> set1 = new HashSet<>(Arrays.asList(1, 2, 3)); Set<Integer> set2 = new HashSet<>(Arrays.asList(3, 4, 5)); // Union Set<Integer> union = new HashSet<>(set1); union.addAll(set2); // Intersection Set<Integer> intersection = new HashSet<>(set1); intersection.retainAll(set2); // Difference Set<Integer> difference = new HashSet<>(set1); difference.removeAll(set2); // Subset boolean isSubset = set1.containsAll(set2); } }
Best Practices
-
Collection Selection
public class CollectionSelection { // Good - Use interface type private List<String> items = new ArrayList<>(); // Good - Use immutable collections when possible private final Set<String> constants = Set.of("A", "B", "C"); // Good - Use appropriate initial capacity private List<String> largeList = new ArrayList<>(1000); // Bad - Using raw types private List rawList; // Avoid raw types // Bad - Using concrete type private ArrayList<String> concreteList; // Use interface type }
-
Thread Safety
public class ThreadSafety { // Good - Use thread-safe collections private final List<String> threadSafeList = Collections.synchronizedList(new ArrayList<>()); // Good - Use concurrent collections private final Queue<String> concurrentQueue = new ConcurrentLinkedQueue<>(); // Good - Use CopyOnWriteArrayList for read-heavy scenarios private final List<String> copyOnWriteList = new CopyOnWriteArrayList<>(); // Bad - Non-thread-safe collection in multi-threaded context private List<String> unsafeList = new ArrayList<>(); // Not thread-safe }
Common Use Cases
-
Caching
public class Cache { private final Map<String, Object> cache = new ConcurrentHashMap<>(); public Object get(String key) { return cache.get(key); } public void put(String key, Object value) { cache.put(key, value); } public void remove(String key) { cache.remove(key); } }
-
Event Queue
public class EventQueue { private final Queue<Event> events = new ConcurrentLinkedQueue<>(); public void addEvent(Event event) { events.offer(event); } public Event processNextEvent() { return events.poll(); } public boolean hasEvents() { return !events.isEmpty(); } }
Testing
- Collection Testing
@Test public void testCollections() { // Test List operations List<String> list = new ArrayList<>(); list.add("Item"); assertEquals(1, list.size()); assertTrue(list.contains("Item")); // Test Set uniqueness Set<Integer> set = new HashSet<>(); set.add(1); set.add(1); assertEquals(1, set.size()); // Test Map operations Map<String, Integer> map = new HashMap<>(); map.put("Key", 1); assertEquals(1, map.get("Key")); assertTrue(map.containsKey("Key")); }
Common Pitfalls
-
Concurrent Modification
public class ConcurrentModification { // Bad - Concurrent modification public void badIteration(List<String> list) { for (String item : list) { list.remove(item); // Throws ConcurrentModificationException } } // Good - Using iterator public void goodIteration(List<String> list) { Iterator<String> iterator = list.iterator(); while (iterator.hasNext()) { String item = iterator.next(); iterator.remove(); } } // Good - Using removeIf public void modernIteration(List<String> list) { list.removeIf(item -> item.startsWith("A")); } }
-
Collection Type Selection
public class CollectionSelectionPitfalls { // Bad - Using ArrayList for frequent insertions private List<String> badList = new ArrayList<>(); // O(n) insertion // Good - Using LinkedList for frequent insertions private List<String> goodList = new LinkedList<>(); // O(1) insertion // Bad - Using HashSet when order matters private Set<String> badSet = new HashSet<>(); // No order guarantee // Good - Using LinkedHashSet when order matters private Set<String> goodSet = new LinkedHashSet<>(); // Maintains order }
-
Memory Management
public class MemoryManagement { // Bad - Keeping references to large collections private static List<byte[]> largeData = new ArrayList<>(); // Good - Using weak references for caches private final Map<String, WeakReference<Object>> cache = new WeakHashMap<>(); // Good - Clearing collections when no longer needed public void cleanup() { largeData.clear(); System.gc(); // Suggest garbage collection } }