Maps in the Java Collection Framework
The Java Collection Framework divides generic data structures into collections, represented by Collection<T>, and maps, represented by Map<T,S>.
Why Programs Need Groups of Objects
Most programs eventually have to deal with many objects at once. A program might keep a list of students in a course, the set of distinct words in a document, or a lookup table that connects a name to a phone number. Writing a separate variable for each of these items would not scale, because the number of items is not fixed and the program needs to add, search, and remove them while it runs.
The Java Collection Framework is the part of Java's standard library that provides ready-made, generic data structures for holding and manipulating groups of objects. Instead of inventing your own way to store a group, you choose from a set of tested structures. This article maps out the framework's main pieces. Later articles on specific classes will have a clear place to fit once you understand this map.
The Question That Chooses Your Tool
A useful way to start is to ask one question about any group you want to store: does the group behave like a sequence of positions, or like a bag of distinct things? The answer determines which part of the framework you reach for. Keep this question in mind, because the rest of the article is organized around it.
Consider two situations. In the first, you record the order in which customers arrive at a counter, and the same customer may come back several times. In the second, you collect the distinct email addresses that have ever signed up, and a duplicate would be a mistake. The first situation is a sequence of positions. The second is a bag of distinct things. Hold on to that difference, because it is what separates a list from a set later in this article.
Collections and Maps as Two Families
The generic data structures in the framework fall into two families. The first family is the collection, which is simply a group of objects. The second family is the map, which pairs objects from one set with objects from another set. A map works like a dictionary that pairs each word with its definition, or like a phone book that pairs each name with a number. Its job is to answer the question of what one thing corresponds to, rather than to hold a plain group.
A collection is a group of objects. A map is a structure that pairs each key from one set of objects with a value from another set of objects, so that a value can be found by its key.
In Java, these two families are described by two parameterized interfaces. Collection<T> represents collections, and Map<T,S> represents maps. The letter T stands for a type parameter that can be any non-primitive type, so Collection<String> is a collection of strings and Collection<Integer> is a collection of Integer objects. Map has two type parameters, which is one reason it is treated separately from collections. This article and the next one concentrate on collections, while maps are left for their own later treatment.
Lookup by Key Versus Position
The difference between the two families shows up in how you find things. In a sequence, an item is located by its position: it is the third item, or the item after the first. In a map, an item is located by a key. You do not ask for the value at position 2. You ask for the value that belongs to a particular key, such as the phone number that belongs to a particular name.
The figure shows a phone book in the spirit of the source's example. Each key on the left is paired with exactly one value on the right, and the question you ask is always 'what corresponds to this key?' A list would answer a different question, namely 'what is stored at this position?' Keeping those two questions apart is the core skill of this article.
What Collection<T> Means for Every Group
Inside the collection family, the framework recognizes two main kinds of collection. A list is a collection whose objects sit in a linear sequence, so it has a first item, a second item, and so on, and every item except the last has an item that directly follows it. A set is a collection built around a single rule: no object can appear in it more than once. The elements of a set are not thought of as having any particular order.
These two ideas are represented by the parameterized interfaces List<T> and Set<T>. Both are subinterfaces of Collection<T>. The practical consequence is that any object that implements List<T> or Set<T> automatically implements Collection<T> as well. Collection<T> holds the operations that every collection shares, while List<T> and Set<T> each add the operations that suit their own kind of group.
Read the figure from the bottom up. An ArrayList<T> must implement the matching list interface. Because List<T> extends Collection<T>, an ArrayList can use every method declared in the list and collection interfaces. The concrete classes are covered in the next article, so for now it is enough to see that the interfaces describe what a collection can do, and the classes describe how it is actually built.
How Lists and Sets Differ
The two kinds of collection differ on exactly the two dimensions the opening question raised: order and duplicates. A list keeps its objects in a linear sequence. A set forbids duplicates and does not attach a particular order to its elements. The table below puts the two side by side.
| Property | List<T> | Set<T> |
|---|---|---|
| Shape of the group | Linear sequence with a first, second, and later items | Bag of elements with no particular order |
| Duplicates | Allowed, the same object may appear more than once | Not allowed, no object can occur more than once |
| Order of elements | Positions are meaningful in the sequence | Elements are not thought of as having a particular order |
| Natural use | Students in a course, arrivals at a counter | Distinct words in a document |
| Relation to Collection<T> | Subinterface of Collection<T> | Subinterface of Collection<T> |
List versus set: order and duplicates
Operations Every Collection Shares
Because a collection can be a list, a set, or something else built on Collection<T>, the operations that the interface defines have to be general. They apply to a collection of any non-primitive type. Suppose coll is an object that implements Collection<T> for some specific type T. The interface then makes the operations below available for coll. Read them as a family of questions you can ask of a group, and as a family of actions you can take on it.
- 1Inspecting: size,
isEmpty, and contains ask about the group's current contents without changing it. - 2Changing: add, remove, and clear modify the group's contents.
- 3Combining:
containsAll,addAll,removeAll, andretainAllrelate the group to another collection. - 4Exporting:
toArraycopies the elements into an array. It returns Object[] rather than T[].
Tracing a Sequence of Collection Calls
The best way to understand these operations is to follow a short sequence of calls and watch the contents change. Start with an empty set of words and apply each call in order. Before you read the trace, predict what the set holds after the fourth call.
A Set of Distinct Words
Start with an empty set of Strings. Apply these calls in order: add apple, add banana, add apple, remove banana. Then ask size() and contains apple. What does the set hold at the end, and what do the queries return?
- 1Start
The set is empty, so its contents are the empty group and its size is 0.
- 2add apple
apple is not yet present, so it is stored. The set now holds apple and its size is 1.
- 3add banana
banana is not yet present, so it is stored. The set holds apple and banana and its size is 2.
- 4add apple again
A set cannot contain an object more than once, so the second apple is not stored. The contents stay apple and banana, and the size stays 2.
- 5remove banana
banana is removed. The set now holds only apple and its size is 1.
- 6size and contains apple
size returns 1 because one element remains. contains apple returns true because apple is still in the set.
The set holds only apple. size() returns 1 and contains apple returns true.
What do you think happens?
Starting from an empty set, you call add with the word apple twice in a row. What is the size of the set afterwards?
Reveal answer
1
A set may not contain the same object more than once, so the second add of apple leaves the set unchanged. Only one apple remains, so the size is 1.
Mistakes Learners Make with Collections and Maps
Common mistake Treating a map as just another collection
What it looks likeAssuming you can loop over a map's pairs with the same operations you use on a list of values.
Why it failsA map pairs keys with values and answers the question of what one thing corresponds to. It sits beside the Collection<T> family rather than inside it.
Do this insteadThink of a map as a lookup structure keyed by one object, and reserve Collection<T> operations for groups of single objects.
Common mistake Expecting a set to keep insertion order
What it looks likeAdding banana then apple to a set and expecting iteration to return banana first.
Why it failsA set's defining rule is no duplicates. Its elements are not thought of as having any particular order.
Do this insteadUse a list when the order of the elements is part of what your program must preserve.
Common mistake Allowing duplicates in a set
What it looks likeCalling add apple twice and expecting the set to hold two copies.
Why it failsNo object can occur more than once in a set, so the second add does not create a second copy.
Do this insteadChoose a set when you need a collection of distinct things, and a list when repeated entries are meaningful.
Common mistake Assuming toArray returns a typed array
What it looks likeWriting code that expects toArray to return an array of the element type T.
Why it failstoArray()returns Object[] rather than T[].Do this insteadExpect an Object[] result and cast or convert elements deliberately where a typed array is needed.
Common mistake Using a primitive type as the type parameter, such as Collection<int>, when the framework requires a non-primitive type such as Integer
Practice Tracing Collection Calls
Start with an empty set of Strings. Apply these calls in order: add red, add blue, add red, remove red, add green. What does the set hold at the end, and what is its size? Trace each call before you answer.
Hints
- Track the contents after every call, not just the first and last.
- A set refuses a second copy of an object it already holds.
- Removing an element takes it out of the set entirely.
Key Takeaways
- A collection is a group of objects, while a map pairs each key with a value and is found by key rather than by position. Maps sit beside the collection family in the framework.
- A list is an ordered linear sequence that may hold duplicates, while a set forbids duplicate elements and does not promise any particular order.
- List<T> and Set<T> both extend Collection<T>, so every list or set provides the general operations defined for all collections.
- The shared operations fall into inspecting, changing, combining, and exporting families, and
toArray()returns Object[] rather than T[]. - To predict a sequence of calls, track the contents after each add, remove, or query, and remember that a set will not hold a second copy of an element.