The Java Collections Framework

A small set of interfaces describes how groups of objects behave, and many implementations trade memory, ordering and speed differently.

What the framework is

The Collections Framework is a set of interfaces describing groups of objects, together with implementations of each and a group of utility classes. Writing code against the interfaces means the concrete class can be changed without touching anything else.

The interface hierarchy

Iterable
   |
Collection
   |
   +-- List      ordered, duplicates allowed, index based
   +-- Set       no duplicates
   |     +-- SortedSet -> NavigableSet
   +-- Queue     usually FIFO
         +-- Deque   both ends

Map          (separate: not a Collection)
   +-- SortedMap -> NavigableMap
Map is deliberately not a Collection. A collection holds elements; a map holds key to value associations. It offers keySet(), values() and entrySet() as collection views instead.

The main implementations

InterfaceImplementationOrderingBacked by
ListArrayListInsertion orderA resizable array
ListLinkedListInsertion orderA doubly linked list
SetHashSetNoneA hash table
SetLinkedHashSetInsertion orderHash table plus links
SetTreeSetSortedA red black tree
MapHashMapNoneA hash table
MapLinkedHashMapInsertion or access orderHash table plus links
MapTreeMapSorted by keyA red black tree
DequeArrayDequeInsertion orderA circular array
QueuePriorityQueueBy priorityA binary heap

Common Collection methods

Collection<String> items = new ArrayList<>();

items.add("java");
items.addAll(List.of("sql", "html"));
items.remove("sql");
items.removeIf(value -> value.length() < 4);

System.out.println(items.size());
System.out.println(items.isEmpty());
System.out.println(items.contains("java"));

items.forEach(System.out::println);
items.clear();

Program to the interface

List<String> names = new ArrayList<>();     // good
ArrayList<String> other = new ArrayList<>(); // needlessly specific

Declaring the variable as List means switching to LinkedList later is a one line change. Declare the most general type that supports what the code actually does.

Immutable collections

List<String> fixed = List.of("a", "b", "c");         // Java 9 and later
Set<Integer> codes = Set.of(1, 2, 3);
Map<String, Integer> ages = Map.of("Meera", 30, "Arun", 27);

List<String> copy = List.copyOf(mutableList);        // an immutable snapshot

// fixed.add("d");   // throws UnsupportedOperationException

These are genuinely immutable, reject null, and are more compact than a wrapped ArrayList. Use them for constants and for anything returned from a method that callers should not modify.

Generics keep collections type safe

List<String> safe = new ArrayList<>();
safe.add("java");
String value = safe.get(0);        // no cast needed

List raw = new ArrayList();        // raw type, avoid
raw.add(42);
String broken = (String) raw.get(0);   // compiles, throws at runtime

Choosing quickly

You needUse
An ordered list with fast access by indexArrayList
Frequent adding and removing at both endsArrayDeque
Unique elements, order irrelevantHashSet
Unique elements in insertion orderLinkedHashSet
Unique elements kept sortedTreeSet
Key based lookupHashMap
Key lookup with sorted keys or range queriesTreeMap
Always retrieve the smallest or largest firstPriorityQueue
A shared map under concurrencyConcurrentHashMap

Legacy classes

Vector, Stack and Hashtable predate the framework. Every method is synchronised, which is slower and does not make compound operations safe anyway. Use ArrayList, ArrayDeque and HashMap, and reach for java.util.concurrent when threads are involved.

Common mistakes

  • Declaring variables with the concrete class instead of the interface.
  • Using raw types and losing compile time checking.
  • Calling add on a list returned by List.of or Arrays.asList.
  • Choosing LinkedList for indexed access, which is linear rather than constant.
  • Using a mutable object as a HashSet element or a map key.

Best practices

  • Declare the interface, instantiate the implementation.
  • Use List.of, Set.of and Map.of for fixed data.
  • Return an unmodifiable view or a copy from getters.
  • Give the expected size to the constructor when it is known and large.
  • Default to ArrayList and HashMap, and change only when a measurement or a requirement says so.

Practice

  1. Why is Map not a subtype of Collection?
  2. Choose a collection for: recently viewed pages, unique tags in alphabetical order, a task queue by priority.
  3. What happens when you call add on List.of("a"), and why is that useful?
  4. Rewrite ArrayList<String> x = new ArrayList<>(); to program to the interface and explain the benefit.
  5. Give one reason not to use Vector in new code.

Conclusion

Learn the four interfaces first, then the trade offs between implementations. Program to the interface, prefer immutable collections for fixed data, and pick the implementation from how the data will actually be used.

Useful resources

Hand picked references for this topic
Written by Lorens Mishra

Default administrator account created by the installer.

Continue reading

All Java notes →

Discussion

0 comments
Sign in to join the discussion.

No comments yet. Be the first to say something.