Collections

List and Set are interfaces that allow you to gather a group objects, and they share a superinterface: Collection.

Consider a deck of cards. It has a given number of elements. You can pinpoint any card by its position in the deck starting at the top. You can shuffle the deck. You can remove one or more cards from any spot, and you can insert them wherever you want. You might even decide to sort the cards, perhaps by rank within suit. And any given card can appear more than once; your deck of cards might be for Canasta, or Uno, or Mille Borneslaissez le bon temps rouler! All this and more you can do with a List.

Now suppose that in your deck, the cards are arranged by value rather than position; you can remove one, but can only insert it in the position it held before. The cards might be sorted, and they might not, but the order is predetermined in either case and the deck can’t be shuffled. Furthermore, only one card of any given value can appear in the deck. But no matter how big the deck, you can find a card really fast. That’s a Set.

Collections implement the Iterable interface, which means you can traverse them using for each.

Frequently Used Collection Methods

Lists and Sets are so similar that you find a lot of the methods you’ll used most frequently in their superinterface, Collection. So, with the understanding that E represents a generic class, here they are.

boolean add(E e)

Adds element e to the Collection–if it can–and returns a boolean indicating whether the Collection changed. The return value will be false if the Collection is a Set that already contains e.

boolean addAll(Collection<? extends E> c)

Adds all the elements in c to the Collection–if it can–and returns a boolean indicating whether the Collection changed.

void clear()

Empties c.

boolean contains(Object o)

Returns true if the Collection contains o.

boolean containsAll(Collection<?> c)

Returns true if the Collection contains every element in c.

boolean isEmpty()

Returns true if the Collection contains no elements; a shorthand for c.size() == 0.

boolean remove(Object o)

Removes a single instance of o from the Collection, and returns true if an element was in fact removed. (Note that “an instance of o” means an element in the Collection for which e.equals(o) is true. See above.) (Also note: if o appears more than once in the Collection, only one occurrence is removed by this call.)

boolean removeAll(Collection<?> c)

Removes all instances of all the elements in c from the Collection, and returns true if any were in fact removed. (And what does it mean for an element in the Collection to be in c? Let’s not always see the same hands….)

boolean retainAll(Collection<?> c)

The opposite of removeAll()–remove all elements from the Collection that don’t appear in c. As always, return true if the Collection changes.

int size()

Returns the number of items in the Collection.

Object[] toArray()
<T> T[] toArray(T[] a)

Returns an array of the elements in the Collection. This method is a bridge between Collections and classes that accept arrays but not collections in their method arguments.

Usually, when using the first form, you’ll cast the result to an array of the desired class, like this:

Integer[] myArray = (Integer[]) myListOfIntegers.toArray();

The second form of toArray() is certainly the more interesting. It’s a means of ensuring the class of the elements in the returned array is known. The argument is an array in which the elements of the Collection are stored, and which is then returned by the call. If the Collection fits into this array with room to spare, the remaining array slots are null. Conversely, if the array isn’t big enough to hold the Collection, a new array of adequate size is allocated to replace it.

You’ll see examples online where the argument to the call is allocated and then passed to toArray(). But given that it’s usually not used for anything else, and if it’s too small it gets reallocated, the typical call looks like this:

    Integer[] myArray = myListOfIntegers.toArray(new Integer[0]);

Iterator

You’ll often find that while iterating over the elements in a Collection, you’ll want to remove one. But if you try to modify a Collection while iterating over it, you’ll be hit with a ConcurrentModificationException. Try this and you’ll see what I mean:

        List<Integer> myList = new ArrayList<Integer>();
        myList.add(1);
        myList.add(2);
        for (Integer value : myList) {

            if (value.intValue() < 2 {
                myList.remove(value);
            }
        }

This applies to any change to the Collection–adding, removing, or moving elements–but element removal is the most common scenario. To accommodate it, the Collection framework provides the Iterator interface, and one more method to acquire one:

Iterator<E> iterator()

Returns (would you believe?) an Iterator.

Not surprisingly, an Iterator returns elements of the Collection. More surprisingly, it provides a means of removing elements at the same time without triggering a ConcurrentModificationException. Here’s how it looks.

        List<Integer> myList = new ArrayList<Integer>();
        myList.add(1);
        myList.add(2);
        for (Iterator iterator = myList.iterator(); 
                  iterator.hasNext();) {
            Integer integer = (Integer) iterator.next();
            if (integer.intValue() < 2) {
                iterator.remove();
            }
        }

As you can see, Iterator has three methods used here:

  • hasNext() — returns true if there are more elements.
  • next() — returns the next element
  • remove() — removes the current element.

Notice how instead of calling remove() on the Collection, we call it on the Iterator.

What You Need to Know

  • The Collections interface is extended by the List and Set interfaces.
  • Collections has several methods common to List and Set, but which behave slightly differently (see the next couple of pages for more).
  • The elements of a List can be duplicated and rearranged, and elements can be added anywhere in the List.
  • Sets can contain only one of each element, and their order is relatively fixed.
  • The equals() method determines whether two elements are duplicates.
  • A Collection cannot be changed directly while iterating through it. However, its Iterator can be used to remove an element indirectly while iterating through a Collection.

Next: Lists