Architecture & DesignUtility of the Collection Framework in Java

Utility of the Collection Framework in Java

Developer.com content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

The Collection Framework, encompassed within java.util, is one of the most important subsystems of the Java API library. Built with utmost sophistication, it contains state-of-the-art technology to manage group objects. The subsequent introduction of generics into the core API group made this complementary library into an indispensable tool to leverage Java production. The Collection Framework is basically an assortment of interfaces and implementation classes that represents high performance implementation of data structures and algorithms. The framework provides miscellaneous utility such as reduced programming effort, interoperability, and code reuse. Since its inception (Java version 1.2), a lot has changed with recent enhancements of APIs, such as leveraging Lambda expressions and Streams into the mainstream framework. The article explores basic features and demonstrates its utility with appropriate example codes.

Collection Framework and java.util Package Overview

The utility package is composed of three basic components: interfaces, its implementation classes which grossly aggregates Collection Framework, and utility classes. The interfaces and implementation classes are cohesive in the sense that one provides the generic abstraction while another binds its utility through properties and methods. They are the representation of data structure and algorithmic implementation. Utility classes (not part of the framework, but within the java.util package), on the other hand, are loosely coupled and provide isolated functionality such as Date, Calendar, Locale, Timer, and so forth.

Collection Interfaces

The Collection interface is designated as the root in the hierarchy of Collection Framework. It’s a generic interface. Generics enable it to be used as an argument type in methods that are inherited by other types of the Collection interface. Though not a true collection, there is another type of interface encompassed within the framework, Map, which provides a set of values mapped by certain keys or, in other words a key, value pair of a collection. The framework thus can be categorized into two groups:

  • Collection interfaces, such as java.util.Collection
  • Map interface, such as java.util.Map

Collection
Figure 1: The framework map

Collection Implementation Classes

Concrete classes that implement collection interfaces are easily recognizable by their names. The format is <name of the Class> + <Interface name>. For example,

Interface Hash Table Resizable Array Balanced Tree Linked List Hash Table + Linked List
Set HashSet   TreeSet   LinkedHashSet
List   ArrayList   LinkedList  
Deque   ArrayDeque   LinkedList  
Map HashMap   TreeMap   LinkedHashMap

These are all generic, with unsynchronized implementation having no restriction on the type of elements they may contain. However, the Collection class provides a set of static synchronized wrappers to make some of the implementation classes thread safe. The fail-fast iterator implementation within the class helps to detect invalid concurrent modification and response failure flags rather than behave erratically. There are a number of abstract classes, such as AbstractCollection, AbstractList, AbstractSet, AbstractMap, and AbstractSequentialList. They provide the basic implementation of the collection framework and leave the rest to the extended classes. The methods of these convenient classes can be overridden to create customized implementation classes. They considerably minimize programmers’ effort in the further extension of the Collection Framework.

Check out a few quick example on the Collection and Map interfaces and their implementation classes.

An Example on List

To store a bunch of reference objects in an ArrayList, we can use the interface called List as follows:

List<String> countries = new ArrayList<>();

The List interface acts as a super abstraction of all the concrete “list” classes, such as ArrayList, LinkedList, and so on. However, List itself is a sub implementation of Collection and Iterable. As a result, the following code snippet is also equally valid.

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

package org.mano.example;

import java.util.ArrayList;
import java.util.List;

public class ListDemo {
   public static void main(String[] args) {
      List<String> heroes = new ArrayList<>();
      heroes.add("Ironman");
      heroes.add("Thor");
      heroes.add("Hawk");
      heroes.add("Hulk");
      // duplicates are allowed
      heroes.add("Ironman");

      System.out.printf("Size = %d, Elements are :%sn",
         heroes.size(), heroes);
      List<String> villains = new ArrayList<>();
      villains.add("Loki");
      villains.add("Hulk");
      villains.add("Black Widow");

      List<String> heroesPlusVillains =
         new ArrayList<>(heroes);
      heroesPlusVillains.addAll(villains);
      System.out.printf("Size = %d, Elements are :%sn",
      heroesPlusVillains.size(), heroesPlusVillains);

      List<String> heroesAlsoVillains =
         new ArrayList<>(heroes);
      heroesAlsoVillains.retainAll(villains);
      System.out.printf("Size = %d, Elements are :%sn",
      heroesAlsoVillains.size(), heroesAlsoVillains);

   }
}

An Example on Set

Set ensures a distinct collection of elements. No duplicates. The addAll() method performs the set Union operation and retainAll() performs the set Intersection operation.

package org.mano.example;

import java.util.HashSet;
import java.util.Set;

public class SetDemo {

   public static void main(String[] args) {

      Set<String> heroes = new HashSet<>();
      heroes.add("Ironman");
      heroes.add("Thor");
      heroes.add("Hawk");
      heroes.add("Hulk");
      // no duplicates! this will not be added
      heroes.add("Ironman");

      System.out.printf("Size = %d, Elements are :%sn",
         heroes.size(), heroes);
      Set<String> villains = new HashSet<>();
      villains.add("Loki");
      villains.add("Hulk");
      villains.add("Black Widow");

      Set<String> heroesUnionVillains = new HashSet<>(heroes);
      heroesUnionVillains.addAll(villains);
      System.out.printf("Size = %d, Elements are :%sn",
      heroesUnionVillains.size(), heroesUnionVillains);

      Set<String> heroesIntersectionVillains =
         new HashSet<>(heroes);
      heroesIntersectionVillains.retainAll(villains);
      System.out.printf("Size = %d, Elements are :%sn",
      heroesIntersectionVillains.size(),
         heroesIntersectionVillains);

   }
}

An Example on Deque

Deque is “double ended queue,” a linear collection of elements where insertion and removal can occur at both ends. The next example shows how deque also can behave as a stack data structure.

package org.mano.example;

import java.util.ArrayDeque;
import java.util.Deque;

public class DequeAsStackDemo {

   public static void main(String[] args) {
      Deque<String> stackOfHeroes =
         new ArrayDeque<>();
      stackOfHeroes.push("Ironman");
      stackOfHeroes.push("Thor");
      stackOfHeroes.push("Hawk");
      stackOfHeroes.push("Hulk");
      stackOfHeroes.push("Loki");
      stackOfHeroes.push("Black Widow");

      while (stackOfHeroes.peek() != null) {
         System.out.println("Top element    :"
            + stackOfHeroes.peek());
         System.out.println("Popped element :"
            + stackOfHeroes.pop());
         System.out.println("Stack contents :"
            + stackOfHeroes);
      }
   }
}

An Example on Map

This interface maps keys to values. Each key must map to only one value and does not allow duplicate keys. However, each distinct key may maps to any number of duplicate values.

package org.mano.example;

import java.util.HashMap;
import java.util.Map;

public class MapDemo {

   public static void main(String[] args) {

      Map<String, String> heroesMapToVillain =
         new HashMap<>();
      heroesMapToVillain.put("Superman", "Lex Luthor");
      heroesMapToVillain.put("Batman", "Joker");
      heroesMapToVillain.put("Spiderman", "Venom");
      heroesMapToVillain.put("Flash", "Sivana");
      heroesMapToVillain.put("Ironman", "Mandarin");

      System.out.println(heroesMapToVillain);
      System.out.println("Size " + heroesMapToVillain.size());
      System.out.println("Arch enemy of Flash : "
         heroesMapToVillain.get("Flash"));

      heroesMapToVillain.forEach((String key, String value) -> {
         System.out.println(key + " arch enemy of " + value);
      });
   }
}

Conclusion

The article is just a rudimentary sketch of the Collection Framework. But, to begin with, these are the basic utilities provided in a nutshell. Another powerful feature of the framework is the application of many types of algorithms on all or a part of collection elements. The algorithms, such as search through collection, sorting, and shuffling of elements of a collection, and the like, comes in quite handy when programming. These ready-to-use algorithms are optimized for performance and should be used when needed rather than writing one from scratch.

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Latest Posts

Related Stories