Collection Framework In Java- Deep Dive

Reading Time: 7 minutes

What is a Collection

A Collection in Java is a group of objects consolidated into a single object.

What is a Framework

A Framework is a group of classes and interfaces which provide a well-defined architecture that is ready to be used anytime and anywhere.

What is Collection Framework in Java

  • Collection Framework in Java is a well-defined architecture for creating, manipulating, and updating objects.
  • It consists of efficient algorithms that are implemented to lower the cost of various operations for instanc search, sort, append, etc.
  • In conclusion they are reusable data structures.
  • Collection Framework was introduced by Java from JDK 1.2

Perks of using the Collection Framework in Java

  • Reduces programming effort: As the collection framework provides useful data structures and algorithms, However it saves us a lot more time to focus on the important areas of our program.
  • Increases program speed and quality: In addition it provides the developers with efficient and high performing implementation of Data Structures and Algorithms
  • Consistent API:  The API consists of various interfaces for instance List, Set, Map, Collection and can be implemented by various classes like ArrayList, HashMap, HashSet, etc.

INTERFACES OF COLLECTION FRAMEWORK

Core Interfaces of Collection Framework

Collection Interface

  • It is the parent/root interface of the collection framework.
  • In addition the Collection Framework also has a parent interface the Iterable Interface
  • The Java platform does not provide a direct implementation of this interface but of subinterfaces such as Set, List, Queue, Deque.
  • It contains various abstract methods which are further implemented by several classes of the collection framework. These methods are:-
    • add(Object) – used to add an object to the collection
    • addAll(Collection c) – adds all the elements from the given collection(given in argument) to a collection
    • clear() – deletes/remove all the elements from a collection
    • contains(Object o) – returns true/false if an element is present in the collection.
    • containsAll(Collection c) – returns true/false if all the elements (present in the argument collection) are present in the collection 
    • equals(Object o) – returns true/false after comparing the values of the given object and the collection.
    • hashCode() – returns the hashcode value of the collection.
    • isEmpty() – returns true/false if the collection is empty.
    • iterator() – returns an iterator pointing to the first element in the collection.
    • max() – returns the maximum value present in the collection.
    • parallelStream() – returns a parallel stream with this collection as its source.
    • remove(Object o) – removes the first occurrence of a given object from the collection.
    • removeAll(Collection c) – removes all the occurrences of a given object from the collection.
    • removeIf(Predicate filter) – removes all the elements of this collection that satisfy the given predicate.
    • retainAll(Collection c) – retains only the elements in the collection that are in the argument collection ‘c’.
    • size() – returns the number of elements present in the collection.
    • spliterator() – used to create a spliterator over the elements in this collection.
    • stream() – returns a sequential Stream with this collection as its source.
    • toArray() – returns an array containing all the elements present in this collection.

Set Interface

  • It is a collection that does not contain duplicate elements
  • We can perform all the basic set operations as in Mathematics such as Union, Intersection, and Difference.
  • The Java platform contains three Set implementations: HashSet, TreeSet, and LinkedHashSet
    • HashSet – stores its elements in a hash table, it is the best-performing implementation out of all, but insertion order is not maintained here.  
    • TreeSet – stores its elements in a red-black tree, orders its elements based on their values; it is substantially slower than HashSet. 
    • LinkedHashSet – implemented as a hash table with a linked list running through it, orders its elements based on the order in which they were inserted into the set (insertion-order).
  • For instance, the implementation of the Set Interface looks like :
import java.util.*;
public class Demo
{
    public static void main(String args[])
    {
        int count[] = {1000,900,800,125,650};
        Set<Integer> set = new HashSet<Integer>();
        try {
            for(int i = 0; i < 5; i++) {
                set.add(count[i]);
            }
            System.out.println(set);

            TreeSet sortedSet = new TreeSet<Integer>(set);
            System.out.println("The sorted list is:");
            System.out.println(sortedSet);

            System.out.println("The First element of the set is: "+ (Integer)sortedSet.first());
            System.out.println("The last element of the set is: "+ (Integer)sortedSet.last());
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }
    }
} 

List Interface

  • The List Interface inherits the Collection interface and is present in java.util package.
  • It helps us to store ordered collection (in sequential / insertion order).
  • Unlike the Set interface, the List interface allows duplicate elements to be present in the collection, and stores objects on an indexing basis.
  • The List interface includes operations for the following:
    • Positional access — manipulates elements based on their numerical position in the list. This includes methods such as get, set, add, addAll, and remove.
    • Search — searches for a specified object in the list and returns its numerical position. Search methods include indexOf and lastIndexOf.
  • There are generally three implementations of List Interface –
    • ArrayList
    • LinkedList
    • Vector
  • For instance , the implementation of the List Interface looks like :
import java.util.*;

class Demo {

    public static void main(String args[])
    {
        List<String> al = new ArrayList<>();

        al.add("item 1");
        al.add("item 2");
        al.add(1, "item 3");

        System.out.println(al);
    }
}

Queue Interface

  • The Queue Interface also inherits the Collection interface and is present in java.util package.
  • This interface uses the FIFO (First in First out) principle of storing and retrieving objects, that is, the objects that are inserted first are the ones to be removed/retrieved first. Just like a queue in front of a cinema hall buying tickets.
  • It is an ordered Collection where elements in the Collection are inserted at the end of the queue and removed from the beginning of the queue.
  • Methods of Queue interface are:-
    • boolean add(object)– used to insert an element into the queue and returns true if the insertion was successful else throws an exception.
    • boolean offer(object)-used to insert an element into the queue and returns true if the insertion was successful else returns false.
    • remove()-used to return and remove the element from the head/front of the queue.
    • poll()-used to return and remove the element from the head of the queue or returns null if this queue is empty.
    • element()-It is used to retrieve but does not remove the head of this queue.
    • peek()-It is used to retrieves, but does not remove the head of this queue, or returns null if this queue is empty.
  • There are generally 2 implementations of Queue Interface in java –
    • LinkedList
    • PriorityQueue
  • For instance , the implementation of the Queue Interface looks like :
import java.util.LinkedList;
import java.util.Queue;

class Demo {

    public static void main(String[] args)
    {
        Queue<Integer> q = new LinkedList<>();

        for (int i = 0; i < 5; i++)
            q.add(i);

        System.out.println("Elements of queue " + q);

        int removedele = q.remove();
        System.out.println("removed element-"+removedele);

        System.out.println(q);

        int head = q.peek();
        System.out.println("head of queue-" + head);

        int size = q.size();
        System.out.println("Size of queue-" + size);
    }
}

Dequeue Interface

  • Dequeue interface supports in the insertion and deletion of objects from both ends i.e from front and back.
  • In addition some methods of Deque Interface includes:-
    • boolean add(object)
    • boolean offer(object)
    • remove()
    • poll()
    • element()
    • peek()
  • Generally there are 2 Implementations of the Deque Interface
    • ArrayDeque
    • LinkedList
  • For instance, the implementation of the Deque interface looks like :
import java.util.*;

public class Demo {
    public static void main(String[] args)
    {
        Deque<String> deque = new LinkedList<String>();

        deque.add("Element 1");

        deque.addFirst("Element 2");

        deque.addLast("Element 3");

        deque.push("Element 4");

        deque.offer("Element 5");

        deque.offerFirst("Element 6");

        System.out.println(deque + "\n");

    }
}

Map Interface

  • This collection is used to store key value pairs, each pair generally known as entry.
  • In addition the Map interface is not a subclass of Collection Interface.
  • There are generally 3 Implementations of Map Interface:-
    • HashMap
    • HashTable
    • TreeMap
  • For instance, the implementation of the Map Interface looks like :
import java.util.*;
class Demo {
    public static void main(String[] args) {
        Map map=new HashMap();
        //Adding elements to map  
        map.put(1,"value 1");
        map.put(5,"value 5");
        map.put(2,"value 2");
        map.put(6,"value 6");
        Set set=map.entrySet();
        
        Iterator itr=set.iterator();
        
        while(itr.hasNext()){
            Map.Entry entry=(Map.Entry)itr.next();
            System.out.println(entry.getKey()+" "+entry.getValue());
        }
    }
}  

Important Classes in Collection Framework

ArrayList

  • ArrayList is an Implementation of the List interface , which is resizable and can store hetrogeneous data types unlike array which is of fixed size and can store only a single type of element.
  • Default size of ArrayList when an object is created is 10 .
  • New size of the ArrayList is decided by the formula
    newCapacity = oldCapacity + (oldCapacity >> 1)
  • Suppose the oldCapacity is 10 , therefore the new capacity will be calculated as follows:-
    newCapacity = 10 + (10 >> 1)
    newCapacity= 10 + (5)
    newCapacity=15
  • In conclusion some important points about ArrayList are:-
    • Allows duplicate elements.
    • Maintains insertion/sequential order.
    • Allows random access through indexes.
    • Array List is preferred over Linked List if there are less/no deletions in the ArrayList as it requires shifting of elements by one place which is a cost overhead.
  • For instance the implementation of the ArrayList goes like :
import java.util.ArrayList;

public class Demo {
    public static void main(String[] args) {
        ArrayList<String> cars = new ArrayList<String>();
        cars.add("Maruti");
        cars.add("Hyundai");
        cars.add("Honda");
        cars.add("Ford");
        System.out.println(cars);
    }
}

LinkedList

  • LinkedList uses a Doubly Linked List internally.
  • It implements List, Queue, and Deque interfaces.
  • Manipulation of elements is faster in LinkedList class as no shifting of elements is needed unlike ArrayList.
  • It cannot be accessed randomly , it has to be traversed from the head of the LinkedList till the Tail in a sequential manner.
  • LinkedList class can contain duplicate elements.
  • It Maintains insertion order.
  • LinkedList class can generally be used as a list, stack or queue.
  • Implementation of the LinkedList looks like :
import java.util.LinkedList;

public class Demo {
    public static void main(String[] args) {
        LinkedList<String> cars = new LinkedList<String>();
        cars.add("Maruti");
        cars.add("Hyundai");
        cars.add("Honda");
        cars.add("Ford");
        System.out.println(cars);
    }
}

HashSet

  • The HashSet class implements the Set interface, backed by a hash table because it is a HashMap instance.
  • HashSet contains unique elements only.
  • No guarantee is made as to the iteration order of the set which means that the class does not guarantee the constant order of elements over time.
  • The initial default capacity of HashSet is 16, and the load factor is 0.75.
  • Hashing provides the best – performing search operations with the worst time complexity O(1) which is constant.
  • NULL elements are allowed in HashSet.
  • HashSet also implements Serializable and Cloneable interfaces.
  • Implementation of the HashSet looks like :
import java.util.HashSet;

public class Demo {
    public static void main(String[] args) {
        HashSet<String> cars = new HashSet<String>();
        cars.add("Maruti");
        cars.add("Hyundai");
        cars.add("Honda");
        cars.add("Ford");
        System.out.println(cars);
    }
}

HashMap

  • It stores the data in (Key, Value) pairs, and you can access them by an index of another type (e.g. an Integer).
  • Java HashMap contains only unique keys.
  • It may have one null key and multiple null values.
  • It is non synchronized.
  • Java HashMap maintains no order.
  • The initial default capacity of Java HashMap class is 16 with a load factor of 0.75.
  • Implementation of the HashMap looks like :
import java.util.HashMap;

public class Demo {
    public static void main(String[] args) {
        HashMap<String, String> capitalCities = new HashMap<String, String>();

        // Add keys and values (Country, City)
        capitalCities.put("India", "Delhi");
        capitalCities.put("Spain", "Madrid");
        capitalCities.put("USA", "Washington DC");
        System.out.println(capitalCities);
    }
}

Stack

  • Java Collection framework provides a Stack class that models and implements a Stack data structure.
  • The class is based on the basic principle of last-in-first-out.
  • In addition to the basic push and pop operations, the class provides three more functions of empty, search, and peek.
  • The class can also be said to extend Vector and treats the class as a stack with the five mentioned functions.
  • Implementation of the Stack Class looks like :
import java.util.Stack;

public class Demo {
    public static void main(String[] args) {
        Stack<Integer> stack=new Stack<Integer>();
        stack.push(1);
        stack.push(2);
        stack.push(3);
        System.out.println(stack.peek());
        stack.pop();
        System.out.println(stack);
    }
}

For more reference , you can visit Oracle Java Documentation : https://docs.oracle.com/javase/tutorial/collections/intro/index.html

Written by 

Prakhar is a Software Consultant at Knoldus . He has completed his Masters of Computer Applications from Bharati Vidyapeeth Institute of Computer Applications and Management, Paschim Vihar . He likes problem solving and exploring new technologies .

Discover more from Knoldus Blogs

Subscribe now to keep reading and get access to the full archive.

Continue reading