Concepts / Maps in the Java Collection Framework

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>.

  • Programming

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.

Definition

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.

Adakey555-0101value for AdaBenkey555-0148value for BenCarakey555-0199value for Caramaps tomaps tomaps to
Given a key such as Ada, which value does the map return, and why does the lookup not depend on where the pair was placed?

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.

Collection<T>operations shared by allcollectionsList<T>ordered sequenceArrayList<T>concrete classSet<T>no duplicate elementsextendsextendsimplements
Which interfaces extend Collection<T>, and how does a concrete class such as ArrayList<T> reach the operations declared at the top?

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.

PropertyList<T>Set<T>
Shape of the groupLinear sequence with a first, second, and later itemsBag of elements with no particular order
DuplicatesAllowed, the same object may appear more than onceNot allowed, no object can occur more than once
Order of elementsPositions are meaningful in the sequenceElements are not thought of as having a particular order
Natural useStudents in a course, arrivals at a counterDistinct 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.

  1. 1
    Inspecting: size, isEmpty, and contains ask about the group's current contents without changing it.
  2. 2
    Changing: add, remove, and clear modify the group's contents.
  3. 3
    Combining: containsAll, addAll, removeAll, and retainAll relate the group to another collection.
  4. 4
    Exporting: toArray copies 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.

Worked example

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?

  1. 1
    Start

    The set is empty, so its contents are the empty group and its size is 0.

  2. 2
    add apple

    apple is not yet present, so it is stored. The set now holds apple and its size is 1.

  3. 3
    add banana

    banana is not yet present, so it is stored. The set holds apple and banana and its size is 2.

  4. 4
    add 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.

  5. 5
    remove banana

    banana is removed. The set now holds only apple and its size is 1.

  6. 6
    size and contains apple

    size returns 1 because one element remains. contains apple returns true because apple is still in the set.

Result

The set holds only apple. size() returns 1 and contains apple returns true.

Start{ } size 0add apple{apple} size 1add banana{apple, banana} size 2add apple again{apple, banana} size 2remove banana{apple} size 1size and containsapplesize 1, truenext callnext callnext callnext callnext call
After each add, remove, or query in the sequence, what does the set hold, and how does its size change?

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?

  • 0

  • 1

  • 2

Reveal answer
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 like

    Assuming you can loop over a map's pairs with the same operations you use on a list of values.

    Why it fails

    A 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 instead

    Think 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 like

    Adding banana then apple to a set and expecting iteration to return banana first.

    Why it fails

    A set's defining rule is no duplicates. Its elements are not thought of as having any particular order.

    Do this instead

    Use 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 like

    Calling add apple twice and expecting the set to hold two copies.

    Why it fails

    No object can occur more than once in a set, so the second add does not create a second copy.

    Do this instead

    Choose 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 like

    Writing code that expects toArray to return an array of the element type T.

    Why it fails

    toArray() returns Object[] rather than T[].

    Do this instead

    Expect 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

MEDIUM

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.