Choosing Collection Types - Java

  1. Which collection is most appropriate for representing each permission group?
    Answer: EnumSet<Permission>, using the enum universe for compact, specialized set operations When a question asks about the most appropriate collection for a specific type, look at three things: the element type, the uniqueness requirement, and the operations being performed. All three point to the same answer here. Java's EnumSet<E> is a specialized Set implementation built exclusively for enum types. Internally, it uses a bit vector — one bit per enum constant — which makes it extraordinarily compact and fast. Union (addAll), intersection (retainAll), and complement (complementOf) operations on an EnumSet run in O(1) time because they reduce to simple bitwise operations on that vector. Since enums are inherently non-null and unique, EnumSet also enforces the "at most once" constraint automatically. Choice A is correct for exactly these reasons. Choice B, HashSet<Permission>, does enforce uniqueness and would technically work, but it's a general-purpose structure with no awareness of enum internals. You lose the bitwise performance optimization and use more memory — it's the right category (Set) but the wrong tool for enum types. Choice C, ArrayList<Permission>, is doubly wrong: lists allow duplicates by design, so "at most once" is not automatically enforced, and list operations do not natively express set math like union or intersection. Choice D, TreeSet<Permission>, enforces uniqueness and adds natural ordering, but enum constants already have a defined ordinal order that EnumSet respects natively, and TreeSet carries O(log n) overhead for no benefit here. Your study tip: whenever you see an enum combined with set semantics, EnumSet is almost always the answer. Java specifically designed it for this scenario, and exam questions frequently test whether you know it exists.
  2. Which collection most directly satisfies these requirements?
    Answer: LinkedHashSet<String>, because it removes duplicates and preserves insertion order When a question asks you to retain unique elements and display them in the order they first appeared — without extra sorting or a separate membership check — you need a collection that handles both jobs simultaneously. That's the key lens for this problem. LinkedHashSet<String> does exactly this. It is a hash-based set, so it automatically rejects duplicate entries (only the first insertion of each tag survives). Internally, it also maintains a doubly-linked list that tracks insertion order, so when you iterate, elements appear in the sequence they were first added. No sorting step, no auxiliary structure — one collection, two guarantees. That makes C the correct answer. Choice A is wrong because HashSet makes no ordering guarantees at all. It does remove duplicates, but the iteration order is unpredictable and based on hash codes — your tags could come out in any sequence, violating the "first-occurrence order" requirement. Choice B is wrong because TreeSet sorts elements according to their natural ordering (alphabetical for strings), not the order they were encountered. It removes duplicates, but it changes the encounter order — directly contradicting the requirement. Choice D is wrong because ArrayList preserves insertion order but does not automatically remove duplicates. You would have to manually check for membership before each insertion, which the passage explicitly rules out ("should not maintain a separate membership collection"). Study tip: Memorize the three main Set behaviors — HashSet (fast, no order), TreeSet (sorted order), LinkedHashSet (insertion order). Exam questions often hinge on knowing which ordering guarantee each provides.
  3. Which collection is the best general-purpose choice for this buffer?
    Answer: ArrayDeque<Token>, using a resizable array for efficient operations at both ends When a question describes a data structure needing efficient insertion and removal at both ends, with no indexed access and a preference for memory efficiency, you should immediately think about the Deque (double-ended queue) interface and how different implementations handle it internally. ArrayDeque<Token> (choice C) is the right tool here. It uses a resizable circular array internally, giving you O(1) amortized performance for addFirst, addLast, removeFirst, and removeLast — exactly the operations the parser needs. Crucially, it avoids allocating a separate wrapper object for every element, keeping memory overhead low. Choice A, ArrayList, is a poor fit because it only offers efficient operations at the tail. Removing or inserting at the front requires shifting every existing element — an O(n) operation that degrades as the buffer grows. Choice B, LinkedList, does implement the Deque interface and supports O(1) operations at both ends, but it allocates a Node object for every single element. The passage explicitly says to avoid per-element node allocation, which rules LinkedList out. Choice D, PriorityQueue, is entirely wrong conceptually — it orders elements by priority and only efficiently gives you access to the minimum (or maximum) element, not arbitrary end-based access. It doesn't model a deque at all. A useful rule of thumb: **prefer ArrayDeque over LinkedList as a deque or stack in Java** unless you specifically need null elements (which ArrayDeque forbids — but the passage says tokens are non-null) or list-style iteration with index access. ArrayDeque is almost always faster and leaner.
  4. Which collection design most directly satisfies these requirements?
    Answer: A TreeMap<String, Integer> mapping each word to its accumulated occurrence count When a problem asks you to both count occurrences and retrieve keys in sorted order without a separate sorting step, you need a data structure that handles both concerns simultaneously — that's the signal to think about sorted maps. TreeMap<String, Integer> is the right tool here. A TreeMap keeps its keys sorted in natural (lexicographic) order at all times, so iterating over it automatically gives you words alphabetically. The Integer value stores each word's count, which you increment on repeated encounters using something like map.put(word, map.getOrDefault(word, 0) + 1). No duplicate entries are created — the existing key is simply updated. This directly satisfies every requirement stated in the passage. Choice A uses a TreeSet<String>, which is sorted but stores only unique elements — it has no mechanism to associate a count with each word. Inserting a word multiple times simply does nothing after the first insertion, so you lose occurrence data entirely. Choice C uses a HashMap<String, Integer>, which can accumulate counts correctly, but HashMap makes no ordering guarantees whatsoever. Iterating over it does not produce lexicographic order, so you'd need a separate sort — exactly what the problem forbids. Choice D uses an ArrayList<String> storing every occurrence, which wastes memory proportional to total word count, requires sorting and manual counting during retrieval, and violates the requirement against using a separate collection or sorting step. A useful pattern to remember: whenever you see "sorted keys + associated values," reach for TreeMap. When you see "unique sorted elements" with no associated data, reach for TreeSet.
  5. Which design most directly implements the required removal order?
    Answer: A PriorityQueue<Job> ordered by priority and then by an increasing submission sequence When a data structure must enforce two-level ordering — primary by priority, secondary by submission time — you need a comparator that encodes both criteria, not just one. A PriorityQueue in Java uses a heap, so it always removes the minimum element according to its comparator. For this scheduler, "minimum" means lowest priority number first, then earliest submission for ties. Option C achieves this by comparing jobs first by priority value, then by a monotonically increasing submission sequence number (e.g., a counter incremented each time a job is added). This guarantees that among equal-priority jobs, the one submitted earlier always has a smaller sequence number and is therefore dequeued first — exactly what the problem requires. Option A is tempting but wrong. A standard PriorityQueue does not preserve insertion order among equal-priority elements; heap rebalancing can surface any tied element first. Relying on insertion order for tie-breaking silently breaks the requirement. Option B uses an ArrayDeque, which preserves submission order but cannot efficiently serve the lowest-priority job without a full scan. It conflates "order of insertion" with "priority ordering," making it O(n) per removal and architecturally mismatched to the problem. Option D uses a TreeSet, which stores only unique elements by its comparator. Since jobs are compared only by priority, two equal-priority jobs would be treated as duplicates and one would be silently dropped — a serious correctness bug, especially since the problem explicitly says multiple jobs may share a priority. Study tip: Whenever tie-breaking is required in a priority queue, always inject a sequence number into the comparator — never trust insertion order to survive heap operations.
  6. Which collection-based design best satisfies these requirements?
    Answer: A ConcurrentHashMap<String, Integer> updated through merge() or compute() When a question asks about concurrent, atomic updates to shared data without a single serializing lock, you should immediately think about Java's java.util.concurrent package — specifically, ConcurrentHashMap and its atomic update methods. ConcurrentHashMap achieves high concurrency by internally partitioning its data into segments (or using node-level locking in modern Java), meaning threads working on different URL keys can proceed simultaneously without blocking each other. Critically, methods like merge() and compute() perform the read-modify-write cycle atomically at the key level, so no visit count is ever lost to a race condition. This is exactly what option D provides. Option A is a classic race condition trap. Calling get() and then put() on a HashMap is not atomic — two threads can both read the same old value, both increment it, and both write back, silently dropping one count. Option B avoids data corruption, but using a single lock for every increment forces all threads to queue up regardless of which URL they're updating. This serializes all updates through one lock, directly violating the stated requirement. Option C misuses CopyOnWriteArrayList, which is designed for read-heavy scenarios where writes are rare. Appending a new element for every page visit creates unbounded memory growth, requires a full-list copy on every write, and gives you no efficient way to count visits per URL — it's the wrong data structure entirely. A useful study pattern: when you see "concurrent writes, no single lock, atomic updates," the answer almost always involves ConcurrentHashMap combined with its functional update methods (merge, compute, computeIfAbsent). Know these methods cold.
  7. Which collection provides the most direct support for all of these operations?
    Answer: TreeMap<Integer, Reading>, using navigational and range-view operations on sensor IDs When a question describes operations like "find the smallest key greater than or equal to X" or "retrieve all entries within a key range," you should immediately think about sorted map navigation — this is testing your knowledge of the NavigableMap interface and its primary implementation, TreeMap. TreeMap<Integer, Reading> keeps keys sorted in natural order and exposes methods designed exactly for this scenario: ceilingKey(k) returns the smallest key ≥ k (the "next ID" lookup), and subMap(from, to) returns a live view of all entries within an interval. These aren't workarounds — they're first-class API methods. That makes D the correct answer. Looking at the distractors: A uses HashMap, which stores entries in no particular order. Finding a neighboring ID or scanning a range requires iterating the entire map — O(n) brute force instead of O(log n) tree traversal. B uses LinkedHashMap, which preserves insertion order, not numeric order. If sensors were registered in random sequence, insertion order tells you nothing about which ID is numerically adjacent. You'd still need full iteration. C reaches for TreeSet<Reading>, but this stores readings, not mappings from ID to reading. To look up by sensor ID you'd have to search inside the set indirectly, and you lose the clean key→value structure that a map provides. A set isn't a map. A useful pattern to memorize: whenever a problem mentions "nearest key," "ceiling/floor," or "key range," TreeMap is almost always the answer — it was built for exactly that navigation.
  8. Which collection design is most appropriate for the cache?
    Answer: A LinkedHashMap<String, Response> configured for access order and eldest-entry removal When designing a bounded cache with least-recently-used (LRU) eviction, you need a structure that tracks access order and lets you automatically remove the oldest-accessed entry when capacity is exceeded. This is a classic Java collections design problem. LinkedHashMap is purpose-built for exactly this scenario. When you construct it with accessOrder = true, it reorders entries from least-recently-accessed to most-recently-accessed on every get() or put(). Crucially, you can override its removeEldestEntry() method to return true when size() > maxSize, triggering automatic eviction of the LRU entry. Option B correctly identifies both of these configuration requirements together, making it the right answer. Option A fails because HashMap has no guaranteed iteration order whatsoever — the internal hash-bucket layout determines traversal, not insertion or access time. You cannot treat that order as meaningful recency. Option C uses a TreeMap, which sorts keys lexicographically (alphabetically by the string key). Removing the lexicographically smallest key has nothing to do with which entry was least recently used — it would evict entries based on their names, not their usage patterns. Option D introduces a LinkedHashSet, which stores only values (responses) without the request-string keys you need for cache lookups, and insertion order ignores subsequent access entirely. A good study tip: whenever a question describes LRU caching in Java, immediately think LinkedHashMap with accessOrder = true and an overridden removeEldestEntry(). These two details go together as a pair — knowing one without the other leads to an incomplete implementation.
  9. Which collection is the most appropriate primary representation for the SKUs?
    Answer: ArrayList<String>, because it preserves duplicates and provides efficient positional access When choosing a Java collection, you need to match the data structure's performance characteristics to the workload's dominant operations. Here, the requirements are: allow duplicates, preserve insertion order, fast appends, fast positional (index-based) access, and only occasional mid-list removal. ArrayList<String> satisfies every requirement. It stores elements in a contiguous array, so appending is amortized O(1) and retrieving by index (get(i)) is O(1). Duplicates are fully supported and insertion order is maintained. This makes A the correct choice. B is tempting but wrong. LinkedList<String> does allow duplicates and preserves order, but indexed access (get(i)) requires traversing the list node-by-node — O(n) time. The question explicitly states positional retrieval is frequent, so LinkedList's primary strength (cheap mid-list removal) doesn't outweigh its expensive random access for this workload. C is incorrect for a fundamental reason: LinkedHashSet<String> is a Set, which means it rejects duplicate elements entirely. Since the same SKU can appear multiple times, a Set is disqualifying regardless of its ordering properties. D is wrong because ArrayDeque<String> does not support indexed access at all — there is no get(i) method. It's optimized for adding/removing at the two ends (front and back), not arbitrary positional retrieval. Strategy tip: On collection-choice questions, always check three things in order: (1) does it allow duplicates? (2) does it support the required access pattern at the right Big-O? (3) does the dominant operation match the structure's strength? Eliminate any option that fails criterion one or two before comparing performance.
  10. Which collection is the most appropriate choice?
    Answer: A CopyOnWriteArrayList<Listener>, accepting expensive writes so that iterators observe a stable snapshot without locking When evaluating concurrent collection choices, focus on three axes: iteration frequency, write frequency, and snapshot consistency guarantees. This question tests whether you can match those requirements to the right data structure. The scenario explicitly calls for frequent reads, rare writes, and lock-free iteration over a stable snapshot. CopyOnWriteArrayList is purpose-built for exactly this trade-off. On every write (add or remove), it creates a fresh copy of the underlying array. Iterators always reference the array snapshot captured at the moment iteration began, so callbacks never need to hold a lock and never see mid-iteration structural changes. This makes A the correct answer. B is a classic trap. Synchronizing an ArrayList makes individual method calls atomic, but iterating without holding the lock between calls creates a race condition — another thread can modify the list between any two steps of your loop, causing ConcurrentModificationException or inconsistent behavior. You would need to lock the entire iteration block, which defeats the "no explicit locking during callbacks" requirement. C is wrong because ConcurrentLinkedQueue is an unordered queue with weakly consistent iterators — they may or may not reflect recent changes. The scenario requires an ordered list and a stable snapshot, neither of which this structure guarantees. D is wrong on two counts: LinkedHashSet prevents duplicates and maintains insertion order, but it is not thread-safe at all. Concurrent iteration and modification would require external synchronization, violating the requirement. As a study tip: whenever a question mentions "read-heavy, write-rare, no locks during iteration," immediately think CopyOnWriteArrayList — that phrase is essentially its design contract.

Start this quiz