The collections framework offers many implementations of the same interface, each with different performance characteristics. Picking correctly is a matter of matching the structure to how you read and write.
Lists and sets
ArrayList wins for indexed access and iteration. Use LinkedList only when
you insert and remove at the ends constantly. HashSet for membership,
LinkedHashSet when order matters.
List<Order> recent = new ArrayList<>(); // fast random access
Set<String> seen = new HashSet<>(); // O(1) contains
Map<String, User> byId = new HashMap<>(); // O(1) lookup
NavigableMap<Instant, Event> log = new TreeMap<>(); // sorted, range queriesConcurrency-safe options
Under contention, reach for the concurrent variants instead of wrapping with
Collections.synchronizedMap.
Map<String, Integer> counts = new ConcurrentHashMap<>();
counts.merge(key, 1, Integer::sum);Takeaways
- Default to
ArrayListandHashMapuntil a profiler says otherwise. - Use
TreeMap/TreeSetwhen you need sorted iteration or range queries. - Size collections up front with an initial capacity to avoid rehashing.
Prefer the interface type (List, Map) in signatures so you can swap
implementations without touching callers.