The classes and interfaces defined within the Collection Framework act as a container for objects; each contained object is called an element of the collection. In essence, an element is a container for group objects. However, in that sense the simple array is an efficient way to store and retrieve group of object references and primitive values. Then, why do we need a collection, after all? There are two primary reasons. Firstly, arrays are fixed in size and cannot expand or shrink dynamically according to the content. Secondly, compile-time type checking does not allow an array to store values of different kinds. In fact, we can pick up numerous advantages/disadvantages if we pursue more on the comparative review. Let’s not do it here; instead, let’s explore the core concepts behind a few of the collection objects in the Java framework.
The Collection Framework
The Collection Framework is composed of three main components, such as:
- Interfaces, which represent a specific type of collection in the framework. There is typically one collection interface defined for every collection type. For example, the List interface, which represents an ordered collection, is the parent interface of all the sub classes and sub interfaces, such as ArrayList, LinkedList, and so forth. Similarly, the Set interface defines the set that does not allow duplicate elements. Its sub implementations are Hashset, TreeSet, and so on. The Map interface represents the collection of elements that map keys to values in a one-to-one mapping fashion.
- Implementation classes represent the concrete implementation of some collection interfaces, such as ArrayList, LinkedList, and the like, which are nothing but the sub implementation of the List interface according to various standard data structures.
- Algorithms classes provide the means to apply different types of algorithms to the collection classes. For example, we often need to search, sort, or copy elements in the collection or maybe convert a collection of one type to another, and so forth. Realize that this type of operation requires a standard implementation of some frequently used algorithms in the library. Algorithms classes supply exactly the required methods that are used readily with collection instances.
The Collection Hierarchy
The collection hierarchy is maintained in the following manner. Figure 1 illustrates a few of the common interfaces in the hierarchy.
Figure 1: The collection hierarchy
The framework also contains many concrete classes that provide the implementation of the collection interfaces. Suppose we want to create an object of List interface. We may instantiate ArrayList in the following manner.
List<String> countries=new ArrayList<>(); countries.add("India"); countries.add("USA"); countries.add("Japan");
We may iterate over the contents in the following manner.
for(String c:countries) System.out.println(c);
Or, we may use an iterator to iterate over the content as follows.
Iterator<String> iter=countries.iterator(); while(iter.hasNext()) System.out.println(iter.next());
Iterator<E> and Enumeration<E> Interfaces
The Iterator<E> interface acts as a cursor to traverse through collection elements. There is another similar interface, called Enumeration<E>. Iterator<E> is a replacement of Enumeration<E> in the collection framework since version 1.2. Enumeration<E> primarily provides a nextElement() method to return successive elements of the series, similar to the Iterator<E> example code above. Iterator<E> is rather a compact version of it. It not only shortened the method names, like hasNextElement(), nextElemen() of Enumeration<E> to hasNext() and next() respectively in Iterator<E>,s but also added another method, called remove(). The remove() method removes the last element returned by the iterator from underlying collection. It is recommended to use Iterator<E> in place of Enumeration<E> in the collection framework.
There is a specific iterator for List, called ListIterator<E>. ListIterator<E> provides methods for bidirectional traversing, element insertion and replacement, apart from normal operations of Iterator<E>. These features are absent in both Iterator<E> and Enumeration<E>.
List<E> and Set<E> Interfaces
Both the interfaces represent a collection of elements but with some specific differences.
List<E> maintains an ordered sequence of elements with a precise control over element insertion point in the collection. Note that, like the arrays list index, it also begins with 0. For example, lists of elements say,
List<String> colors=new ArrayList<>(); colors.add("Red"); colors.add("Green"); colors.add("Green"); //Duplicate? OK colors.add("Blue"); colors.add("violet");
To insert elements at a specific index, we may use an overloaded add method as follows.
There is a similar method, called set. It can be used as follows.
colors.set(1, "light green");
The main difference between these two methods is that the eadd method inserts elements at a specified index and, if an element exists at that index, it simply shifts all the elements one step forward in the collection sequence and then inserts the new element. The set method, on the other hand, is used specifically to replace an existing element at the specified place in the index with a new element.
Searching an element in an unknown index in the list can be done with the following method:
int indexOf(Object o)
Searching is a costly operation because, in many operations, it will implement a linear search. A linear search in a large collection can bog down performance.
The set<E> maintains a collection of elements that are unique, with no control over the insertion point by the index.
Set<String> aves=new HashSet<>(); aves.add("Hornbill"); aves.add("swallow"); aves.add("sparrow"); aves.add("kite");
Unique elements in the collection means that following two add operation with duplicate elements will actually add only one element in the collection. The duplicate one will simply be ignored. This also means that there can be only one null element in the collection.
aves.add("macaw"); aves.add("macaw"); //Duplicate? No effect
Observe, List<E>, however, allows duplicate elements in the collection.
Because Set<E> is modeled upon mathematical set abstraction, the set operations such as union, intersection, and set-difference (minus) are applicable as follows:
Set<String> A=new HashSet<>(); aves.add("A"); aves.add("B"); aves.add("C"); aves.add("D"); aves.add("E"); Set<String> B=new LinkedHashSet<>(); aves2.add("A"); aves2.add("B"); aves2.add("F"); aves2.add("G");
A.addAll(B); // A = AUB
A.retainAll(B); // A = A∩B
Set Difference Operation
A.removeAll(B); // A = A-B
To understand the difference, the keywords to remember List<E> and Set<E> are as follows:
- List<E>: index-able collection
- Set<E>: no-duplicate collection
Map<K, V> Interfaces
It’s a key, value pair collection. That means that every element in the collection is uniquely identified/mapped by a key. This collection can be visualized easily as a table with two columns. The first column represents the keys and the second column contains the values associated with the keys. For example, the key value map of a collection of employee names and their phone numbers can be represented as follows.
Each key is mapped to exactly one value. The key must be unique, although there is no restriction on duplicate values. There can be only one null value as the key but multiple values as the null value. A simple implementation can be as follows.
Map<String,String> map = new HashMap<>(); map.put("Tom", "(111)123-4567"); map.put("Dick", "(222)123-7890"); map.put("Harry", "(333)373-3703"); String dp = map.get("Dick"); System.out.println("Map: " + map); System.out.println("Map Size: " + map.size()); System.out.println("Map is empty: " + map.isEmpty()); System.out.println("Map contains Dick key: " + map.containsKey("Dick")); System.out.println("Dick Phone: " + dp); System.out.println("Dick key is removed: " + map.remove("Dick")); map.clear(); // removes all elements in the map
The collection framework contains numerous interfaces and classes that are used with a wide range of collection types. The List<E>, Set<E>, and Map<K, V> are the basic building blocks in the collection hierarchy. All the collection-related interfaces and classes are grouped together in the java.util package. The utility class called Collections contains many static methods of different types of algorithms that can be applied to collection elements on a specific purpose. As always, the best source of more information on collection framework is the Java Documentation itself.