A checkout service keeps open orders in an ArrayList and asks open.contains(order) on every payment. A warehouse worker queue is a LinkedList because “it is a queue.” A session cache is a Hashtable because it is “thread-safe.” All three compile. All three are the wrong type for the hot operation.

Name the hot operation, then pick the JDK type whose contract makes that operation cheap — and whose nulls, order, and thread story you can live with. This page is the series decision guide, not a recap of every class. Glossary (Collection vs Map, optional operations, fail-fast, views vs copies) lives on the Collections Roadmap. Layout theory lives on the Data Structures Roadmap. Jump to the row that matches the pain you have today.

The four questions before new

Ask these in order. The first one you cannot answer is the one to stop on.

  1. What is the hot operation? Index i, uniqueness, key lookup, either end, next-best, sorted range, hand-off to another thread.
  2. Do you need encounter order? Insertion, access, sort, or none.
  3. Do you need nulls? Most queues and every concurrent type in this series say no.
  4. Does another thread look? If yes, you want a java.util.concurrent type or you do not share. A synchronized wrapper is not the default.

If you cannot name the hot operation, you are not ready to name a class. Start there.

Indexed list

get(i) is cheap only when the slots sit in one array. A linked list makes you walk.

JobReach forAvoid as a defaultPost
Growable indexed listArrayListLinkedList, VectorArrayList: The Default List and When Growth Bites
List contract, subList, RandomAccessList + ArrayListTreating every List as O(1) getList: Index, SubList, and Random Access as a Type
List and deque on the same nodesLinkedList (rarely)LinkedList as the default ListLinkedList: Nodes When ArrayList and ArrayDeque Already Lost
Tiny unmodifiable listList.ofArrays.asList when you meant immutableCollection Factories

ArrayList is the default list. Reach for LinkedList only when you already hold a ListIterator and splice, or you truly need List plus Deque in one object. For stack and queue work, skip both and use ArrayDeque.

Uniqueness

Uniqueness is not List.contains. That method walks.

JobReach forAvoid as a defaultPost
Unique elements, no orderHashSetArrayList.contains as a setHashSet and LinkedHashSet
Unique and insertion orderLinkedHashSetHashSet plus a side listHashSet and LinkedHashSet
Unique and sortedTreeSetSorting a HashSet on every readNavigable Collections
Unique enumsEnumSetHashSet<OrderStatus>EnumSet and EnumMap
The Set contractSetAssuming iteration orderSet: Uniqueness Without Scanning the List

The Set post is the contract. HashSet is the usual delivery. LinkedHashSet is the sequenced hash set. TreeSet is sorted, O(log n), and rejects null under natural order.

Key → value

A map is mappings, not a Collection. Lookup by key is not a scan of pairs.

JobReach forAvoid as a defaultPost
Key → value, no orderHashMapHashtable; a list of pairsHashMap: The Default Map and What the Table Actually Does
Insertion or access order, LRULinkedHashMapA list plus a map “to be sure”LinkedHashMap
Sorted keys, ceiling/floorTreeMapSorting HashMap keys on every readNavigable Collections
Enum keysEnumMapHashMap<OrderStatus, …>EnumSet and EnumMap
== identity, not equalsIdentityHashMapHashMap with records you meant as valuesIdentityHashMap and WeakHashMap
GC-able keysWeakHashMapUsing it as an LRU cacheIdentityHashMap and WeakHashMap
Shared mapConcurrentHashMapHashtable, synchronizedMapConcurrentHashMap
Shared and sortedConcurrentSkipListMapA locked TreeMapConcurrent Queues and Skip Lists

HashMap is the default map. computeIfAbsent and merge belong on the Map contract. Do not mutate a key after insert. WeakHashMap is not a cache with a max size — LinkedHashMap removeEldestEntry is the bounded LRU.

Ends, stacks, queues, next-best

Indexes are the wrong primitive when the job is the ends.

JobReach forAvoid as a defaultPost
Stack / queue / deque, one threadArrayDequeStack, LinkedList as a queueArrayDeque
offer / poll vs add / removeQueue / DequeTreating a queue as a ListQueue and Deque
Next-best, not FIFOPriorityQueueSorting the whole list on every insertPriorityQueue
Hand-off between threadsBlockingQueueBusy-wait on a ConcurrentLinkedQueueBlockingQueue
Non-blocking unbounded endsConcurrentLinkedQueueCalling .take() on it (there is none)Concurrent Queues and Skip Lists

ArrayDeque is the default for stack and queue in one thread. PriorityQueue is a heap: iterator order is not sorted. Bounded producer-consumer is ArrayBlockingQueue or a capped LinkedBlockingQueue — the uncapped default capacity on LinkedBlockingQueue is a trap the blocking post names.

First, last, reversed

Java 21 gave ordered containers a shared vocabulary. HashSet and HashMap are not in it.

JobReach forAvoid as a defaultPost
First / last / reversed on an ordered typeSequencedCollectionIndex arithmetic on every ordered typeSequenced Collections

If you needed a sequence on uniqueness, that is LinkedHashSet, not HashSet iteration order.

Unmodifiable, views, and the methods around the types

JobReach forAvoid as a defaultPost
Unmodifiable copyList.copyOf / Map.copyOf / List.ofunmodifiableList wrapping a live ArrayList you still mutateCollection Factories
Sort, shuffle, asList, empty/singletonCollections / ArraysArrays.asList when you meant List.ofCollections and Arrays
Iterate a collection, land in anotherstream() + Collectors / Stream.toList()parallelStream() plus add to an outer ArrayListSpliterator and Collectors
Shared data you did not mean to pick—Vector, Stack, HashtableLegacy Collections

Collections.synchronizedList still needs the iterator locked by you. Prefer CopyOnWriteArrayList when reads dominate and writes are rare, or ConcurrentHashMap for a shared map.

Shared with another thread

JobReach forAvoid as a default
Shared mapConcurrentHashMapHashtable, Collections.synchronizedMap
Snapshot iteration, rare writesCopyOnWriteArrayListCOW as a general ArrayList
Blocking FIFO bufferArrayBlockingQueue or a capped LinkedBlockingQueueUncapped LinkedBlockingQueue (capacity Integer.MAX_VALUE)
Next-best and another thread waitsPriorityBlockingQueueA locked PriorityQueue
Available after a delayDelayQueueTreating it as a scheduler
Rendezvous (no buffer)SynchronousQueueA queue of size 1 “to be sure”
Transfer now if a consumer waitsLinkedTransferQueueBusy-wait on ConcurrentLinkedQueue
Blocking both endsLinkedBlockingDequeSharing an ArrayDeque
Non-blocking unbounded endsConcurrentLinkedQueue / ConcurrentLinkedDequeCalling .take() on them (there is none)
Concurrent sorted map / setConcurrentSkipListMap / ConcurrentSkipListSetA synchronized TreeMap

Whole-table synchronized is not how you share a collection in 2026. Confine to one thread, or name a concurrent type.

Interview lens

This page is the “which type?” round. Interviewers want the default, then the exception.

QuestionHonest answer
Default List / Set / Map / deque?ArrayList, HashSet, HashMap, ArrayDeque.
When LinkedList?Almost never as a List. As a deque, ArrayDeque wins.
When TreeMap instead of HashMap?You need sorted keys or ceiling/floor, and you can pay O(log n).
When ConcurrentHashMap instead of HashMap?Another thread looks. Not because “we might need it later.”
Vector vs ArrayList?Vector is legacy whole-table lock. It is not the thread-safe ArrayList.
Queue for workers?One thread: ArrayDeque. Two threads: BlockingQueue.
Unique + order?LinkedHashSet, not HashSet iteration.

What to draw. Four defaults. Then one extra for order (LinkedHash*), one for sort (Tree*), one for share (ConcurrentHashMap), one for hand-off (BlockingQueue).

Wrong answer: “Always use Vector and Hashtable in a web app because they are synchronized.”

Cheat sheet

Hot op first    index | unique | key | ends | next-best | sorted | hand-off
Then            order? nulls? another thread?

Defaults        ArrayList  HashSet  HashMap  ArrayDeque
Order           LinkedHashSet / LinkedHashMap
Sorted          TreeSet / TreeMap          O(log n)
Enum universe   EnumSet / EnumMap
Share a map     ConcurrentHashMap          no nulls
Hand-off        BlockingQueue              put/take
Snapshot reads  CopyOnWriteArrayList       writes copy the array

Skip            Vector, Stack, Hashtable, LinkedList-as-default
Not a cache     WeakHashMap
Not a sequence  HashSet / HashMap iteration

Do:

  • Pick the type for the operation that runs in the hot path.
  • Program to List / Set / Map / Deque. new the class that matches the four questions.
  • Prefer factories (List.of, copyOf) when the collection should not grow.

Don’t:

  • Scan a list to fake a set or a map.
  • Share an ArrayList or HashMap across threads because tests passed with one worker.
  • Treat HashSet order, PriorityQueue iteration, or WeakHashMap as an LRU.

Wrap-up

The Collections Framework is a small set of contracts and a larger set of deliveries. ArrayList, HashSet, HashMap, and ArrayDeque cover most application code. Order, sorting, an enum universe, another thread, or a blocking hand-off are the reasons to leave those defaults — not a longer class name for the same job.

The Collections Roadmap is the glossary and the living catalog. This page is the “which new?” test. If the hot operation is a layout question more than a JDK type question, start at Pick the Right Structure.