Collections in Java

In this article, we are going to learn about collections in Java. This is a new topic for us but is highly important, useful and interesting. Here we will briefly learn about the different collections in Java along with ArrayList and HashMap in detail as these are the ones that you will be using most of the time. So, let’s get started.

What is a collection and collection framework?

The Java platform provides a collection framework. A collection is simply a group of objects represented as a single unit. The Java collection framework is an architecture used for representing, storing and manipulating data of the collections. The Java Collection framework defines several classes and interfaces such that a group of objects is represented as a single unit. The collection framework allows collections to be manipulated independently of their implementation details. These may sound a little intimidating, but don’t worry, everything will be clear after taking a look at a few examples.

Why do we need a collection framework?

  • Before the Java Collections Framework was introduced, the standard collections used were Arrays, Hashtables and Vectors. They did not have any common interface and lacked a unifying theme. Each of these structures had different syntax and methods for accessing and manipulating data, which caused problems and inefficiency. 

  • The introduction of Collection Framework in JDK 1.2 unified all the collections and provided a common interface to be used with them. It allowed different types of collections to work in a similar manner and with high interoperability.

  • The framework is highly performance-oriented and efficient and can easily extend and adapt to all collections.

  • It reduces programming effort since data structures and algorithms are ready to be used and not have to be written again.

  • It reduces the effort required to learn, design and implement APIs. API stands for Application Program Interface and they contain a basic set of interfaces such as Collection, set and list and all the classes that implement these interfaces have some common methods.

  • It increases the quality of our program and a developer could focus on the application of these collections instead of worrying about their implementation.

Java Collection Framework Hierarchy

Here’s an image showing the most important interfaces and classes of the collection framework-

Collections
Collections in Java
  • From the above figure, it is clear that the root interfaces are Collection and Map. 

  • The core collection interfaces are Collection, List, Queue, Set, SortedSet, Map and SortedMap.

  • Set is a special type of Collection and SortedSet is a special type of Set and so on. Map is not a true Collection since it has a different branch but it is fully integrated with Collections.

  • All the core Collection interfaces are generic. For example, the declaration of the Collection interface is public interface Collection<E>. This tells us that the Collections are of generic type and when we declare an instance of this interface, we have to specify the type of object contained in the Collection. It can be any of the data types provided by Java.

  • Collections that do not support modification operations such as add and remove are known as unmodifiable. And those which support are known as modifiable collections.

  • Like Strings in Java, Collections can also be immutable, i.e., no permanent changes actually take place. Those which are not immutable are known as mutable collections.

  • Collections can either be fixed-size or variable-size. Some collections also have some restrictions which we will discuss later in the article. 

  • All the collections are contained in the java.util package and the most basic interfaces are java.util.Collection and java.util.Map

Collections in Java

  1. Iterable: The iterable interface is the root interface for all collection classes. The Collection interface extends the Iterable interface and therefore all its subclasses also implement the Iterable interface.

  2. Collection: It is the interface that is implemented by all the classes in the collection framework. It is the root of the Collection hierarchy. The foundation of the Collection framework is built in the Collection interface and thus, it contains all the declarations of the methods that the Collections will have. The syntax of creating objects of all Collection framework classes are the same CollectionClassName<dataType> objName = new CollectionClassName()

  3. List: The List interface extends the Collection interface. A List is a data structure that is used to store an ordered collection of objects. Duplicate values are allowed. There are various methods in it that are used to insert, delete or access elements and contain some methods other than the ones in Collection interface.

  4. ArrayList: ArrayList is a class that implements the List interface. We will learn about ArrayList in detail later in the article.

  5. Vector: Vector is a class that implements the List interface. It is a dynamic array and is similar to ArrayList with a few differences. It is synchronized and contains many methods that are not part of the Collections framework. An example of creating a Vector object is Vector<int> v = new Vector(5)

  6. Stack: A Stack is a subclass of Vector and is a data structure that implements the Last-In-First-Out principle. This class contains the basic Stack operations push, pop, empty, peek and search methods.  It contains a single constructor to create an empty stack. This class has a lot of applications while using data structures and algorithms.

  7. LinkedList: The LinkedList class implements List and Deque interfaces. It is a linear data structure that consists of nodes instead of elements. Manipulation and access of data are done with the help of pointers. It can contain duplicate elements and manipulation is faster than arrays since shifting is not required. It is unsynchronised and is used for implementing a list, stack or queue.

  8. Queue: A Queue is another core interface of the Collection framework and extends the Collection interface. It is a data structure that implements the First-In-First-Out principle. It contains methods for operations like the addition and removal of data. The most frequently used implementations of Queue are LinkedList, ArrayBlockingQueue and PriorityQueue.

  9. Deque: The Deque is a double-ended queue. It is a data structure that can be used both as a queue or a stack and supports common methods like addition, deletion, etc. 

  10. PriorityQueue: The PriorityQueue is a class that implements the Queue class which means that it already contains all the methods used in Queue. A priority queue is used when we need to process the objects based on priority. This is the main difference between a Queue and a PriorityQueue.

  11. Set: A Set is a collection that does not allow duplicate elements. It is mainly used when the uniqueness of elements is to be considered. It is implemented by HashSet, LinkedHashSet, and TreeSet. Set has various methods such as add, remove, retain, clear, contains and hashcode of the elements. 

  12. HashSet: This class implements the set interface with a Hash table. It is the best performing implementation among the three and is not concerned about maintaining the order of iteration. The mechanism hashing is used to store the elements in the hash table. It inserts elements based on their hashcodes.

  13. LinkedHashSet: The LinkedHashSet is just like HashSet but is ordered and uses a doubly-linked list for storing elements. It allows us to iterate through the elements in their order of insertion, unlike HashSet. The performance of LinkedHashSEt is a bit slower as compared to HashSet since the former uses LinkedList.

  14. SortedSet: The SortedSet interface extends the Set interface and as the name suggests, it provides a total ordering of the Set. It maintains its elements in ascending order and provides a few additional operations to access the elements.

  15. TreeSet: The TreeSet implements the set interface and maintains the ascending order of elements. It uses a tree structure for the storage of data. It is non-synchronized and does not allow null elements.

  16. Map: A Map is an object that stores data as key-value pairs, i.e., it maps keys to values. Every value is associated with a unique key. A Map cannot contain duplicate keys. However, it can contain duplicate values. These values can be searched, updated or deleted on the basis of keys. The Map interface contains various operations to be performed on the data such as put, get, remove, containsKey, size, putAll, values, etc. The java Map interface is not a part of the Collection interface. Therefore, it behaves a little differently from the rest. Maps are great when we want to use key-value pairs such as a dictionary.

  17. HashTable: HashTable is a class that implements the Map interface and maps keys to values. It does so by implementing a hash table for storing the key-value pairs. It is synchronized. Here, the hash code of the key is used as the index to access values. 

  18. HashMap: HashMap is almost equal to HashTable with the difference that HashMAp is unsynchronized. We will learn about HashMap in detail later in the article.

  19. LinkedHashMap: It is a HashMap built using a doubly-linked list with predictable iteration order. Traversal is easier in this case and order is maintained. 

  20. SortedMap: It extends the Map interface and implements the key-value pairs just like a Map object but it orders all the keys by their natural ordering or as specified by a Comparator provided at the time of creation. 

  21. TreeMap: TreeMap implements the SortedMap interface and stores the key-value pairs in the form of a tree structure. It is an efficient way of sorting key-value pairs. It is non-synchronized and maintains the ascending order of elements.

Time to learn about ArrayList and HashMap in detail.

ArrayList

ArrayList implements the List interface and uses the Array data structure to store elements. It is dynamic in nature and can grow and shrink in size as and when required. This is the reason why ArrayList is preferred over Array. 

There are 3 constructors in the ArrayList class, and therefore, 3 different ways of creating an ArrayList. They are:

  1. public ArrayList(): It creates an empty list with initial capacity 10.
  2. public ArrayList(int initialSize): It creates an empty list with the specified initial capacity.
  3. public ArrayList(Collection c): It creates a list initialised with the elements of the specified collection.

The syntax of creating an ArrayList object is-

ArrayList<ObjectType> objName = new ArrayList<ObjectType>();


An example of creation of ArrayList of type String is-

ArrayList<String> names = new ArrayList<String>();


The elements in an ArrayList always have to be objects. Thus, if we want to create an ArrayList of integers, we use Integer instead of int, Double instead of double, and so on.

ArrayList<Integer> numbers = new ArrayList<Integer>();


Apart from the methods inherited from the List interface, ArrayList contains other methods. Let us now take a look at some of the most important and frequently used methods.

1. void add(): This method is used to add elements to the ArrayList. This method adds elements to the end of the ArrayList by default. Let’s add names to our ArrayList names that we created above-

names.add("Harry");
names.add("Hermione");
names.add("Ron");
names.add("Ginny");
names.add("Malfoy");


We can also add elements at specified locations in the ArrayList-

names.add(3,"Tom");


2. boolean addAll(Collection C): It adds all the elements of the Collection C to the end of the ArrayList in the order specified by the iterator.

3. Object get(int index): It is used to access the element at the specified index. Example-

names.get(1); // Hermione
names.get(5); // Tom


4. boolean contains(): This method checks whether the ArrayList contains the specified element or not. Example-

names.contains("Ginny"); // true
names.contains("Riddle"); // false


5. int size(): This method returns the size of the ArrayList.

names.size(); // 6

6. void ensureCapacity(int requiredCapacity): This method is used to increase the capacity of the ArrayList, if necessary, to ensure that the ArrayList can hold at least the number of elements specified in the argument.

7. void remove(): This method is used to remove specific elements from the ArrayList.

  • void remove(int index): Removes element from the specified index. Example: names.remove(3); // Ginny will be removed
  • void remove(objectType element): Removes the first occurance of the element in from the ArrayList. Example: names.remove(“Tom”); // Tom will be removed
  • void removeAll(): Removes all the occurrences of the element from the ArrayList.

8. void set(int index, ObjectType element): This method is used to update an element from the specified index. Example – names.set(0, “Potter”); // Harry will be replaced by Potter 

9. sort(): The Collection interface provides a method sort() to arrange the elements in ascending order. An example of the sort method used in ArrayList is-

Collections.sort(names); //sorts ArrayList in alphabetical order


10. int indexOf(ObjectType element): This method is used to return the index of the element specified in the argument. Example: names.indexOf(“Ron”); // will return 2

11. boolean isEmpty(): This method is used to check if the ArrayList is empty or not.

12. clear(): This method is used to remove all the elements from the ArrayList. 

13. Object[] toArray(): This method returns an array containing all of the elements in the ArrayList in the correct order. Throws NullPointerException if the specified array is null.

14. void trimToSize(): Trims the capacity of the ArrayList instance to its current size. This helps in saving memory.

Printing the ArrayList

There are many ways to print the ArrayList. 

1. Using iterator interface: We can use an object of Iterator to iterate through the ArrayList. Example-

Iterator itr = names.iterator();
while(itr.hasNext()) {
	System.out.println(itr.next());
}	


2. Using for loop or while loop: We can use any loop and the get() to traverse through the elements of the ArrayList. Example-

for(int i = 0; i<names.size(); i++)
	System.out.println(names.get(i));


3. Using a for-each loop for traversal: Example-

for(String i : names)
	System.out.println(i);


We can use any type of object for the ArrayList. It may be an in-built object or user-defined one. ArrayList is unsynchronized and is a bit slow but is very useful due to its dynamic nature.

HashMap

HashMap is a class which provides the basic implementation of the Map interface. HashMap uses a Hash table for storing data in key-value pairs. A method known as hashing is used to store these keys and their values. HashMap doesn’t allow duplicate keys but it allows duplicate or null values. It can allow only one null key. It internally contains a linked list to store the values and traverse through them. HashMap does not maintain order and is unsynchronized but can be explicitly made synchronized by a SynchronizedMap() provided in the Collection interface. 

There are two factors on which the performance of the HashMap is dependent. They are Initial Capacity and Load factor. Initial Capacity is simply the number of key-value pairs(buckets) it can hold which is specified at the time of the creation of the HashMap object. Load factor decides when rehashing should be done. Rehashing is a process of increasing the capacity.

Constructors of HashMap

  1. HashMap(): It is the default constructor that creates a HashMap instance with initial Capacity 16 and load factor 0.75.
  2. HashMap(int initialCapacity): It creates a HashMap instance with the specified initial capacity and default load factor.
  3. HashMap(int initialCapacity, float loadFactor): It creates a HashMap instance with the specified arguments. 
  4. HashMap(Map map): It creates a HashMap instance with the same key-value pairs as the specified Map.

Creation of HashMap object

The syntax for creation of a default Hashmap object is-

HashMap<KeyType, ValueType> objName = new HashMap<>();

An example is-

HashMap<String, Integer> week = new HashMap<String, Integer>();
HashMap<Integer, Double> hmap = new HashMap<Integer, Double>();


Now let’s take a look at the important methods defined in the HashMap class-

1. void put(K, V): This method is used to add items to the HashMap. Example-

week.put("Sunday", 1);
week.put("Monday", 2);
week.put("Tuesday", 3);


2. valueType get(Key): It returns the value associated with the specified key. Example-

week.get("Sunday"); // will return 1


3. void remove(Key): It removes the key-value pair of the key specified.

4. void clear(): It removes all the elements from the HashMap.int size(): It returns the number of items in the HashMap.

5. set keySet(): It returns the set of all the keys present in the HashMap.

6. set values(): It returns a set of all the values present in the HashMap.

7. boolean containsKey(Key): It returns true is the specified Key is present in the HashMap else returns false.

8. boolean containsValue(Value): It returns true if the specified value is present in the HashMap else returns false.

9. boolean isEmpty(): It returns true if the HashMap is empty else false.

10. Set entrySet(): Returns a set view of the HashMap.

11. boolean equals(Object obj): It is used to compare the specified Object with the Map.

12. boolean replace(Key K, OldValue, NewValue): It replaces the old value with a new value and returns true if correctly done.

Printing and Iterating

We usually use for-each loop for iteration through the HashMap.

//Printing keys only
for(String i : week.keySet()) 
	System.out.println(i);

//Printing values only
for(String i : week.values())
	System.out.println(i);
	
//Printing both
for(String i : week.keys())
	System.out.println("key: " + i + " value: " + week.get(i));


HashMap is an efficient technique for searching and storing data since it is very fast due to hashing. It is the most frequently used implementation of Map interface so, wherever we need to use key-value pairs, HashMap is what we use.

Conclusion

We have now come to the end of this article. It was a long one but we learnt so much about Collections. In Java, Collections play a very important role in storing and manipulating data in an efficient way. We came to know what collections are, why we need a Collection Framework in Java, the framework Hierarchy, short descriptions of all the Collections in Java and the implementation and application of ArrayList and HashMap. Hope you guys have understood the topics clearly and if you need more help or want to try these out yourself, there’s always Google. 

This was the last article and with this, we have come to the end of our Core Java tutorial. I hope it was fun learning a new language. Keep on practising until you master it. All the best!

Leave a Comment