April flash sale

Java Collection Interview Questions and Answers

Java Collection is a framework that provides a set of interfaces and classes to store and manipulate groups of objects. Collections allow developers to work with groups of objects as a single entity and provide various functionalities such as sorting, searching, filtering, and iteration. Some of the commonly used interfaces in the Java Collection framework include List, Set, Map, and Queue. Whether you’re a beginner or appearing for a more advanced Java Collection interview, our set of questions and their detailed explanations will help you prepare for the interview more confidently. The questions have covered various topics like Java Collection framework, List, Set, and Map interfaces, Synchronized and concurrent collections, Performance considerations and more.

  • 4.5 Rating
  • 31 Question(s)
  • 25 Mins of Read
  • 3854 Reader(s)

Intermediate

The collection is an API, introduced in JDK 5. Initially, the collection got released with some of few classes with Vector, Stack, Hash table, Array. The collection is an API which has got various implementation of data structure List, Set, Stack, Queue, Map. The available implementation classes can group the N no of objects without specifying the size. Collection API provides the growable object. All these classes are a growable class which increases the size at runtime.

Expect to come across this popular question in java collection interview questions.

The collection framework provides all the implementation of the available data structures, which reduces the development time. All the classes are growable classes, that means we don’t need to provide the size at compile time. Later release Collection framework also starts supporting the Generics. Collection framework also starts supporting the thread collection class like ConcurrentHashMap and CopyOnWriteArrayList. Code quality also getting improved by using well-tested collection API. Reduce code maintenance by using the collection framework. Collection API also provides the utility class called Collections. Enhance the reusability and interpretability of the code base.

1. Set

  • Set is a collection, which avoids containing duplicate elements.
  • This interface models the mathematical set abstraction and is used to represent sets, such as the deck of cards.
  • Set doesn’t maintain the order.
  • HashSet, LinkedHashSet or TreeSet are the implementations of the Set interface.
  • Set also adds a stronger contract on the behaviour of the equals and hashCode
  • Code Snippet: Set set = new HashSet();

2. List

  • The list follows the ordered collection and can contain duplicate elements.
  • One can access any element from its index.
  • The list is somewhat like array however length is changing at runtime.
  • List interface doesn’t provide the thread safety.
  • ArrayList, LinkedList, CopyOnWriteArray are the implementation of List interface.
  • Code Snippet: List list = new ArrayList();

3. Queue

  • Queue interface is a part of collection framework extend the collection interface.
  • A queue is used to insert elements at the end of the queue and removes from the beginning of the queue. It based on the FIFO concept.
  • The queue is inherited from the Collection framework, so it supports all the method like insertion and deletion.
  • ArrayBlockingqueue and PriorityQueue are the most widely used implementation classes.
  • public class PriorityQueue<E> extends AbstractQueue<E> implements Serializable  
  • Code Snippet: Queue queue = new PriorityQueue();

Java collection framework(high level overview)

This is a frequently asked question in Java collection interview questions and answers.

  1. Dequeue:
    • The Deque interface provides the facility of using double ended queue means we can use either end to push and pop the elements.
    • ArrayDeque is one of the implementations.
    • Unlike Queue, we can add or remove elements from both sides.
    • Null elements are not allowed in the ArrayDeque.
    • ArrayDeque is faster than LinkedList and Stack.
    • CodeSnippet: Deque<String> deque=new ArrayDeque<String>();  
  2. SortedSet
    • This interface extends Set and provides a total ordering of its elements.
    • The implementation class of the SortedSet interface has an implementation of the Comparable interface so that all the elements in the set can mutually comparable.
    • SortedSet by default do the sorting on ascending order.
    • TreeSet is an implementation of a sorted set interface.
    • Code Snippet: SortedSetsortedSet = new SortedSet();
  3. NavigableSet:
    • NavigableSet is responsible for a navigable set in Java Collection Framework.
    • The NavigableSet interface inherits from the SortedSet interface.
    • It behaves like a SortedSet with the exception that we have navigation methods available in addition to the sorting mechanisms of the SortedSet.
    • For example, NavigableSet interface can navigate the set in reverse order compared to the order defined in SortedSet.
    • The classes that implement this interface are, TreeSet and ConcurrentSkipListSet
    • Code snippet: NavigableSet set = new

A map is an object that maps keys to values. The map works on key-value pair principal. A map doesn't allow to contain duplicate keys. Each key can map to at most one value. HashMap, TreeMap, ConcurentHashMap are the implementation of the map. The map has three kind of collection like key, values and key values and it doesn’t implement the collection interface that is the reason the Map interface doesn’t fall in the collection hierarchy. The order of the map depends on the specific implementation classes.

Code Snippet:  Map map = new HashMap();.

The best use case is when we need to specify the pairing between the object and store accordingly.

A staple in Java collections coding interview questions, be prepared to answer this one.

1. HashMap:

  • HashMap is an implementation of map interface, where it's utilizing the hashing algorithms to calculate the index position using hashcode() values from the keys.
  • HashMap guaranteed the time complexity of O(1) for insertion and searching.
  • Code Snippet: Map map = new HashMap();  
  • Use case: Sometimes when the data is huge and at the same time, we need faster insertion and retrieval because the time complexity is O(1) and regardless considering the order of the data, I would say that in this case, one can use the HashMap.

2. LinkedHashMap

Linked HashMap is an implementation of the map interface along with the hashing algorithms it also supports the ordering according to the insertion order.

Code Snippet: Map map = new LinkedHashMap();

Use Case: LinkedHashMap is useful whenever we need to maintain the ordering of keys to match the ordering of insertion. along with that when we need to handle the caching mechanism.

3. TreeMap

TreeMap is an implementation of the map interface, where it follows the Tree data structure which is RedBlack Tree implementation and follows the natural order  

Code Snippet: Map map = new TreeMap();

Use case: One can use the TreeMap when we need the natural ordering, or we need to sort the record alphabetically or ascending and descending order.

TreeMap

Characteristic
HashMap
TreeMap
LinkedHashMap
Time complexity to search and insert
O(1)
O(log n)
O(1)
Iteration Order
Random
Sorted order according to the natural ordering
Sorted order according to the insertion order
Is Null key support
Allowed
Not allowed
Allowed
Is thread safe
No, we need to collections utility to make thread safe
No, we need to collections utility to make thread safe
No, we need to collections utility to make thread safe
Interface
Map
Map, SortedMap, NavigableMap
Map
Data structure support
LinkedList

DoublyLinkedList
Application
Normal processing, faster retrieval ConcurrentHashMap we can use as per the need
Required when we do the sorting and navigable features need as per the use case.
Required when we need to maintain the insertion order and when we need to implement the LRU cache.
Key requirement
equals() and hashcode() method need to overwritten
Along with equals() and hashcode(), the comparator to implement.
equals() and hashcode() method need to overwritten
Syntax
public class HashMap extends AbstractMap
implements Map, Cloneable, Serializable
public class TreeMap extends AbstractMap implements
NavigableMap, Cloneable, Serializable
public class LinkedHashMap extends HashMap
implements Map

Characteristic
ArrayList
LinkedList
Internal datastructure
Internally ArrayList uses a dynamic array to manipulate the record
LinkedList works on the doubly linked list data structure.
Modifying the data
Insertion and deletion in the start and middle of the array list if slow
Insertion and deletion in the middle of the linked list is fast as comparison of LinkedList
Implementation
ArrayList is implemented only List interface
LinkedList implements both List and Deque interface
Searching performance
Searching is fast in ArrayList, all its need the index position to get the desired value
In LinkedList searching is slower, because it needs to traverse the entire list
Complexity
In ArrayList, the search complexity is O(1)
In LinkedLis,t the search complexity is O(n)
Declare and adding the data in the list
List<String>arrayList=new ArrayList<String>();//creating arraylist
arrayList.add("Java");
arrayList.add("Spring");    
arrayList.add("Junit");    
arrayList.add("jdbc");  
List<String>linkedList=new LinkedList<String>();//creating linkedlist
linkedList.add("James");//adding object in linkedlist
linkedList.add("Serena");    
linkedList.add("Swati");    
linkedList.add("Junaid");  
Searching of the data in the list


Deletion of the data in the list


Use case
One can use the ArrayList when we the logic require the searching and getting off the record frequently
One can use the LinkedList, when logic requires, frequent data insertion and deletion.

A common question in Java collections questions, don't miss this one.


HashsetTreeSet
CharacterHashSet doesn’t follow any order and doesn’t ensure that the order we inserted elements do we get the elements in the same order.In TreeSet it follows the natural ordering of the elements
Null operationHashSet permits null valuesTreeSet doesn’t allow any null values, if it found then its throws null pointer exception
SpeedHashSet provides constant time to perform all the basic operation on elements such as search insert, remove, its O (1)TreeSet performance is lower than the HashSet, the time complexity is O(Log(n))
FunctionalityHashSet provides basic functionality to perform the operationTreeSet provides extra functionality such as pollFirst(), pollLast(), first(), last(), ceiling(), lower()
Memory ConsumptionHashSet doesn’t compare the elements and its spread the elements across the memory according to the key.TreeSet has a better memory management, if two entries found near or same Treeset will place in next to each other.
InheritanceHashSet inherit the normal set interface below is the signature
implements java.util.Set, java.lang.Cloneable,

TreeSetimplements navigable Set below is the signature style java.util.NavigableSet, java.lang.Cloneable, java.io.Serializable {

Code
import java.util.HashashSet;
class HashashSetProgram{
 public static void main (String [] args) {
HashashSet<String>hashSet = new HashashSet<String>();
hashSet.add("Java");
hashSet.add("spring");
hashSet.add("hibernate");
hashSet.add("thread");
hashSet.add("linux");
    // Duplicate removed
hashSet.add("spring");
    // Displaying HashashSet elements
System.out.println("HashashSet contains: ");
for(String set : hashSet){
System.out.println(set);
    }
 }
}

import java.util.TreeSet;
class TreeSetProgram{
 public static void main (String [] args) {
    // Create a TreeSet
TreeSet<String>treeSet = new TreeSet<String>();
    //add elements to TreeSet
treeSet.add("Java");
treeSet.add("linux");
treeSet.add("hibernate");
treeSet.add("spring");
treeSet.add("thread");
    // Duplicate removed
treeSet.add("linux");
    // Displaying TreeSet elements
System.out.println("TreeSet contains: ");
for(String set : treeSet){
System.out.println(set);
    }
 }
}
OutputSpring
Java
Thread
Java
linux
Java
Linux
Hibernate
Spring
thread

List is an ordered collection of elements and hence it maintains insertion order of elements while Set doesn't maintain any ordering of the elements. List allows duplicate elements while Set doesn't allow any elements. List is index-based, i.e.we can access elements in List based on Index, whereas we can’t access Set using index. Concrete implementation of List Interface are ArrayList, LinkedList, etc. Concrete implementation of Set implementations are HashSet, LinkedHashSet, TreeSet etc. we can insert any number of null values in List. But we can have only a single null value at most in Set. ListIterator can be used to traverse a List in both directions (forward and backward) however it cannot be used to traverse a Set. Using Iterator, we can traverse the elements in Set and List.

In your program, if you try to insert the elements and retrieve the elements in the same order, then you should use LinkedList. Complexity for inserting an element is O(n) in case of LinkedList.

If you don't need to maintain the insertion or deletion order, then you can use ArrayList. Also, the complexity of insertion and deletion in an ArrayList is O(1) as this data structure is indexed. An element can directly be accessed in ArrayList using index.

Code Snippet:

Insertion in an ArrayList can be done in 2 ways:

add(E e) => Which appends the element at the end of the list.
add(int index, E e) => Add the element at the specified position in the list
       ArrayList<String> arrayList = new ArrayList<String>();
       arrayList .add("Programming");
       arrayList .add(1, "Java");
       arrayList .add("Python");
       arrayList .remove(1);  // Remove value at index 1, considering the indexing starts from 0

 Insertion/ deletion in LinkedList:

       LinkedList linkedListObj = new LinkedList();
       linkedListObj.add("Artificial Intelligence");
       linkedListObj.add("Blocked Chain");
       linkedListObj.add("Academic");
       linkedListObj.remove("Blocked Chain");

Iterator is an interface. It is present in java.util package. It has the following functionalities:

  • hasNext() : Boolean
  • next() : E
  • Code snippet to show how to use Iterator:
public static void main(String[] args)
{
       List<String> list = new ArrayList<String>();
       list.add("Apple");
       list.add("Banana");
       list.add("Orange");
       list.add("Guava");
       // Iterator to traverse the ArrayList
       Iterator iterator = list.iterator();
       System.out.println("ArrayList elements : ");
       while (iterator.hasNext())
           System.out.print(iterator.next() + " ");
       System.out.println();
}

Stack is a data structure which follows the principle of LIFO (Last In Fast Out) OR FILO (First In Last Out), i.e element inserted at last will be the first to be removed from the stack.

Queue is a data structure which follows the principle of FIFO (First In First Out), i.e. element inserted at first to the list will be the first to be removed from the list.

For Stack, insertion and deletion of an element are termed as PUSH and POP while for Queue, insertion, and deletion of an element are termed as  ENQUEUE and DEQUEUE.

In Java, Stack is a class present in java.util package and Queue is an interface present in java.util package.

One of the most frequently posed Java collection programming questions, be ready for it.

  • Arrays of String to List

Arrays class of java.util package contains asList() which helps to convert an array of strings to a List. This method bridges between array-based and collection-based APIs. The returned List is serializable and implements RandomAccess

  • Below is the code snippet :
Public static void main (String[] args) {
String[] names = { " Kolkata", "Bangalore", "Canada", "Australis" };
List<String> nameList = names.asList();
System.out.println(nameList);
for(String name : names )
{
System.out.println(name);
}
}
Using toArray() of List method, we can convert List of Strings to Array of Strings
  • List of String to Array of String
Public static void main (String[] args) {
List<String> list = Arrays.asList("Bangalore", "Kolkata");
String[] nameArray = list.toArray(new String[0]);
System.out.println(Arrays.toString(nameArray));
}

You can see the below code snippet:

// class to sort hashmap by values
public class SortByValue{
   public static HashMap<String, Integer> sortByValue (HashMap<String, Integer> hm)
   {
       List<Map.Entry<String, Integer> > list =
              new LinkedList<Map.Entry<String, Integer> >(hm.entrySet());
       Collections.sort(list, new Comparator<Map.Entry<String, Integer> >() {
           public int compare(Map.Entry<String, Integer> o1,  
                              Map.Entry<String, Integer> o2)
           {
               return (o1.getValue()).compareTo(o2.getValue());
           }
       });
       HashMap<String, Integer> tempMap = new LinkedHashMap<String, Integer>();
       for (Map.Entry<String, Integer> element : list) {
           tempMap.put(element.getKey(), element.getValue());
       }
       return tempMap;
   }
   // Driver Code
   public static void main(String[] args)
   {
       HashMap<String, Integer> hm = new HashMap<String, Integer>();   
       // enter data into hashmap
       hm.put("Computer Science", 93);
       hm.put("Physics", 88);
       hm.put("Chemistry", 69);
       hm.put("Database", 95);
       hm.put("Operating System", 90);
       hm.put("Networking", 79);
       Map<String, Integer> map = sortByValue(hm);
       // print the sorted hashmap
       for (Map.Entry<String, Integer> en : map.entrySet()) {
           System.out.println("Key = " + en.getKey() +  
                         ", Value = " + en.getValue());
       }
   }
}
  • Convert Map Keys To List
Map<String, Integer> map = new HashMap<String, Integer>();
map.put(“Apple”, 1 ) ;
map.put(“Orange”, 2 ) ;
map.put(“Guava”, 3 ) ;
map.put(“Potato”, 4 ) ;     
List<String> listKeys = map.keySet().stream().collect(Collectors.toList());         
listOfKeys.forEach(System.out::println);
  • Convert Map Values To List
Map<String, Integer> map = new HashMap<String, Integer>();
map.put(“Apple”, 1 ) ;
map.put(“Orange”, 2 ) ;
map.put(“Guava”, 3 ) ;
map.put(“Potato”, 4 ) ;          
List<Integer> listOfValues = map.values().stream().collect(Collectors.toList());         
listOfValues.forEach(System.out::println);
  • Interface java.lang.Iterable Iterable is the root interface of the collections API
    • Interface Collection
  • Interface List

List is a collection of unordered elements. It contains duplicate elements also.

  • Interface Set

Set is a collection of ordered elements. It doesn’t contain any duplicate elements.

  • Interface Queue

Queue interface available in java.util package and extends Collection interface

  • Interface Map
    • Interface SortedMap

Map is a collection of (key,value) pair.

We can traverse the elements in a List using Iterator in a forward direction. Using ListIterator, we can traverse the elements in the forward and backward direction both. Iterator can be used in these collection types: List, Set, and Queue.

ListIterator can be used in List collection only.

Iterator has the following functionalities:

  • boolean hasNext()
  • E next()
  • void remove()

ListIterator has the following functionalities:

  • void add (E e)
  • boolean hasNext()
  • boolean hasPrevious()
  • void remove()

Iterator can only perform remove operation while traversing the elements in a collection. If we try to add elements, it will throw ConcurrentModificationException.

ListIterator can perform add, remove operation while traversing the elements in a collection. We won’t get any exception while adding element using ListIterator.

Expect to come across this popular question in Java collection interview questions for experienced.

We can iterate a List in two ways:

  • for-each loop
  • Using Iterator

Here is the code snippet:

public static void main(String[] args) {
List<String> stringList = new ArrayList<>();
//using for-each loop
for(String str : stringList){
   System.out.println(str);
}
//using iterator
Iterator<String> it = stringList.iterator();
while(it.hasNext()){
   String obj = it.next();
   System.out.println(obj);
}
}
  • Comparable: 

A comparable object is capable of comparing itself with another object. The class itself must implement the java.lang.Comparable interface to compare its instances.

Comparable has compareTo(Object o) to sort the objects

Consider a Product class that has members like, productId, productName, ManufacturerYear. Suppose we wish to sort a list of Products based on year of productId. We can implement the Comparable interface with the Product class, and we override the method compareTo() of Comparable interface.

  • Comparator: 

A comparator object is capable of comparing two different objects. The class is not comparing its instances, but some other class’s instances. This comparator class must implement the java.util.Comparator interface.

Comparator has compare(Object o1, Object o2) to sort the objects.

class Product implements Comparable<Product>
{
   private int productRating;
   private String productName;
   private int mfgYear;
   private double price;
   // Used to sort movies by mfgYear
   public int compareTo(Product pr)
   {
       return this.mfgYear - pr.mfgYear;
   }
   // Constructor
   public Product(int pr, String pn, int mY, double price)
   {
       this.pr = pr;
       this.pn = pn;
       this.mY = mY;
this.price = price;
   }
   // Getter methods for accessing private data
   public double getPrice() { return price; }
   public String getProductName()   { return productName; }
   public int getMfgYear()      { return mfgYear; }
   public int getProductRating() { return productRating; }
}
// Class to compare Products by ratings
class ProductRatingCompare implements Comparator<Product>
{
   public int compare(Product m1, Product m2)
   {
       if (m1.getProductRating() < m2.getProductRating())
return -1;
       if (m1.getProductRating() > m2.getProductRating())
return 1;
       else return 0;
   }
}
// Class to compare Products by product name
class ProductNameCompare implements Comparator<Product>
{
   public int compare(Product m1, Product m2)
   {
       return m1.getProductName().compareTo(m2.getProductName());
   }
}

Advanced

Hashmap compose with an array of node and Node is an object of a class which has following below object:

  • HashKey of int type
  • Key of type K
  • Value of type V
  • Reference of next Node

Let’s see how it all together works, first will approach to hashing process, which uses a hashing algorithm to generate index position-taking as an input of hashkey value of the corresponding object. Let’s look on below code snippet:

Class KeyDemo {
String key;
KeyDemo(String key){
This.key = key;
}
@Override
 public int hashCode()
 {
    return (int)key.charAt(0);
 }
 @Override
 public booleanequals(Object obj)
 {
   return key.equals((String)obj);
 }
}

The moment, when we passing the key object in the put method of hashmap like below

Map map = new HashMap();
map.put(key, value);

The HashMap calling the hashcode() method of object and get the 10 digit numeric value, which eventually passing to the hashing algorithms which is like below syntax

f(h(hashCode (key) % array_length)) = {index position} of hashmap, ,where x is a hashcode value, according to the index position the hashmap insert the value object in the corresponding bucket. HashMap uses the linkedlist to store the value object.

HashMap

Below is the implementation of HashMap, where K is the Key and V is the Value, usually the initial capacity is always 16, whenever the map get instantiated and it's computed every time when its reach to a threshold value which is 0.75,

public class Map<K, V> {
   private Entry<K, V>[] buckets;
   private static final int INITIAL_CAPACITY = 1 << 4; // 16
   private int size = 0;
   public Map() {
this(INITIAL_CAPACITY);
   }
   public Map(int capacity) {
this.buckets = new Entry[capacity];
   }
   public void put(K key, V value) {
       Entry<K, V> entry = new Entry<>(key, value, null);
       int bucket = getHash(key) % getBucketSize();
       Entry<K, V> existing = buckets[bucket];
       if (existing == null) {
           buckets[bucket] = entry;
           size++;
       } else {
           // compare the keys see if key already exists
           while (existing.next != null) {
               if (existing.key.equals(key)) {
existing.value = value;
                   return;
               }
               existing = existing.next;
           }
           if (existing.key.equals(key)) {
existing.value = value;
           } else {
existing.next = entry;
               size++;
           }
       }
   }

The retrieval process of HashMap is, it has to compute the hashcode whenever the get method call which along with the key, using the key, hashmap computes the index position and equal() plays the vital role to identify the correct object from the bucket or LinkedList. Below is the code snippet for same.

public V get(K key) {
   Entry<K, V> bucket = buckets[getHash(key) % getBucketSize()];
   while (bucket != null) {
       if (bucket.key.equals(key)) {
           return bucket.value;
       }
       bucket = bucket.next;
   }
   return null;
}

There is a collision issue happening which is usually taken care of by collision detection and collision prevention.

The collision problem is, whenever the hash function returns the same index position for the different key, then a collision occurs. The collision detection technique is also called collision detection.

collision prevention technique

Now, let's move to the collision prevention technique. The HashMap uses below algorithms to resolve the collision problem.

  1. Linear probing: This algorithm is default implementation to resolve the collision issue, lets see how it works, in this approach HashMap implements LinkedList data structure to store the elements, while collision occurs HashMap first check the index position using the hashcode of key, if it founds same index position and conflict with other keys, then HashMap calls the equals() method of the Key, so using the composite approach of hashcode() and equals() method, the HashMap start traversing on linear(one by one) elements in LinkedList, because HashMap also maintain the equal() method of information in the linked list to store the elements, using both this approach the HashMap finds the correct elements in the linked list.
  2. Below is the code snippet forget operation in the HashMap. Where get method takes the Key object as input and returns the Value object as V and checking whether the Key is null, then calculate the hash code and iterate over the hash map.
public V get(Object key) {
   if (key == null)
   return getForNullKey();
   int hash = hash(key.hashCode());
   for (Entry<K , V> e = table[indexFor(hash, table.length)]; e != null; e = e.next) {
       Object k;
       if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
           return e.value;
   }
   return null;
}

One of the most frequently posed collection interview questions for experienced, be ready for it.

The time complexity of HashMap to getting the elements is usually from O(n) to O(Log(n)), when there is no collision occurs and the worst case is O(n) to O(1), because when collision occurs and HashMap needs to travel till the end of the linked list. Now in the case of putting the elements in HashMap, is always O(1). Below classes is the implementation of Map interface.

  • LinkedHashMap,
  • HashMap
  • Hashtable,
  • WeakHashMap,
  • IdentityHashMap,
  • ConcurrentHashMap,
  • TreeMap, and
  • EnumMap.

However, as per the memory performance basis, the WeakHashMap gives better performance. But it doesn’t provide the thread safety if one needs to achieve the thread safety, to achieve the thread safety one needs to use the ConcurrentHashMap because it provides thread safety without using synchronization.

Initial capacity in HashMap plays a vital role in the allocation of memory as we know that HashMap works on the key-value pair, that means the HashMap needed a large amount of memory to maintain the key and value, to overcome these challenges the HashMap works on the principal to allocate the optimum size, whenever the HashMap get instantiated, that is called Initial capacity, which is usually a 16.

Now let’s focus on load factor, so there is a concept of threshold value, which is always a 0.75 size of the total capacity, the significance of this is whenever any new record gets inserted in the HashMap, the HashMap computes the allocated size of the HashMap, then it checks whether this size reaches the threshold value then HashMap increased the size of 75% from the current size to augmented the storage for next incoming record.

Fail fast and fail-safe is one of the prominent features of collection iterator framework, which works on the principle of modification of a record in the collection object while iterating over it, however, both have a different way to handle.

Fail Fast iterator: The fail-fast iterator checks, If there is modification happening while iterating over the elements, it will check and throw the exception called a ConcurrentModificationException. Typically the fail-fast iterator implements the volatile counter whenever the iterator gets instantiated this counter embedded to the iterator and in the case of any modification it will increment the counter if it is greater than one it will throw the exception. Below is the code snippet for same.

public class FailFastDemo{ 
    public static void main(String[] args) 
    { 
        Map<String, String> capital = new HashMap<String, String>(); 
        capital.put("Delhi", "India"); 
        capital.put("Moscow", "Russia"); 
        capital.put("New York", "USA"); 
        Iterator iterator = capital.keySet().iterator(); 
        while (iterator.hasNext()) { 
            System.out.println(capital.get(iterator.next())); 
            // in this code we are modifying the element to Map 
            // exception will be thrown on next call 
            // of next() method. 
            capital.put("Istanbul", "Turkey"); 
        } 
    } 
}

Fail-safe Iterator: Fail safe iterator is a part of the java.util.concurrent api, it works on the principal of the cloning, actually in this case it creates the clone of the object and iterate over it, if any modification happens, it wouldn’t touch to the actual object and continue to iterate without failure. One issue with the fail-safe iterator is, it doesn’t guarantee that updated elements will be reflected in the iteration.  And another issue is it consumes more memory than fail fast because it requires to create the clone, whereas failing fast works only original one.

The CopyOnWriteArrayList and ConcurrentHashMap is the example of fail-safe iterator.

Below is the code snippet for same.

ConcurrentHashconcurrentHashMap<String, Integer> concurrentHashMap = new ConcurrentHashconcurrentHashMap<>();
concurrentHashMap.put("First", 10);
concurrentHashMap.put("Second", 20);
concurrentHashMap.put("Third", 30);
concurrentHashMap.put("Fourth", 40);
Iterator<String> iterator = concurrentHashMap.keySet().iterator();
while (iterator.hasNext()) {
    String key = iterator.next();
    concurrentHashMap.put("Fifth", 50);
}

Comparable is part of java.lang package, which is used to compare another object from itself, the way it works that the class needs to implement the comparable interface and need to override the compare method which takes another object as a parameter, this scenario is when we use it to implement natural comparison with another object, it could be any user-defined class.The return type of compareTo() method is int type which could return three different values (-1, 0, 1) Below is the code snippet for same.

/* This class implements the Comparable interface which uses as a comparison between two objects.
*   Class has override the compareTo() method
*/
class StudentComparableDemo implements Comparable<StudentComparableDemo>{  
int rollno;  
String name;  
int age;  
StudentComparableDemo(int rollno,String name,int age){  
this.rollno=rollno;  
this.name=name;  
this.age=age;  
}  
//This method performs the comparison of object of Student using the attribute the age which compares with the age of other student object.
public int compareTo(StudentComparableDemo st){  
if(age==st.age)   // If age of both the object is same it will return 0
return 0;  
else if(age>st.age)  
return 1;  
else  
return -1;  
}  
}  

The comparator is an interface of java.util package which predominantly works on collection library, the best use case is to compare the two different collection object. The sorting can perform using Collections utility class.

Below is the code representation.

import java.util.*;
import java.lang.*;
import java.io.*;
// This class to represent an Employee domain object which eventually works store the employee domain information.
class Employee
{
   int empid;
   String name, address;
   // Constructor
   public Employee(int empid, String name,
                              String address)
   {
       this.empid = empid;
       this.name = name;
       this.address = address;
   }
   // Used to print Employee details in main()
   public String toString()
   {
       return this.empid + " " + this.name +
                          " " + this.address;
   }
}
/* This classes responsible for performing the compare and sorting the employee result according to the empid
*/
class SortbyId implements CompemployeeListator<Employee>
{
   // Used for sorting in ascending order of
   // roll number
   public int compemployeeListe(Employee a, Employee b)
   {
       return a.empid - b.empid;
   }
}



/* This classes responsible to performs the compare and sorting the employee result according to the empid
*/  
class Sortbyname implements CompemployeeListator<Employee>
{
   // Used for sorting in ascending order of
   // roll name
   public int compemployeeListe(Employee a, Employee b)
   {
       return a.name.compemployeeListeTo(b.name);
   }
}
// The Driver class perform the operation on sorting the employee object
class Main
{
   public static void main (String[] employeeListgs)
   {
       employeeListrayList<Employee> employeeList = new employeeListrayList<Employee>();
       employeeList.add(new Employee(111, "bbbb", "london"));
       employeeList.add(new Employee(131, "aaaa", "nyc"));
       employeeList.add(new Employee(121, "cccc", "jaipur"));
       System.out.println("Unsorted");
       for (int i=0; i<employeeList.size(); i++)
           System.out.println(employeeList.get(i));

Collections are the utility class which has the sort method takes an input of list of the employee and implemented comparator class use to perform the sorting.

       Collections.sort(employeeList, new SortbyId());
       System.out.println("\nSorted by empid");
       for (int i=0; i<employeeList.size(); i++)
           System.out.println(employeeList.get(i));
       Collections.sort(employeeList, new Sortbyname());
       System.out.println("\nSorted by name");
       for (int i=0; i<employeeList.size(); i++)
           System.out.println(employeeList.get(i));
   }
}

Iterator and Enumerator are two different classes in the collection api, which is used to perform the traversing to a collection object. The use case is whenever there is a need to traverse the elements and there is no need to add and update the elements in the list than its good to go with the Enumerator, an enumerator is basically a legacy class which is a support to all the legacy classes Vector, Stack, HashTable.

Whereas Iterator is an advanced way to handle the traversing when there is a requirement to modify the elements in the collection object than we can use the Iterator. Iterator can support all kinds of Collection class like List, Map and Set.

Iterator can provide fail-safe and fall fast according to the corresponding collection class.

Below is the code snippet for Enumeration

import java.util.*;

public class EnumerationIteration
{
 public static void main(String args[])
 {
     Vector Barista = new Vector();
   Barista.add("Chipotele"); Barista.add("Starbucks"); Barista.add("Dunkin Donuts");
// Creating the enumeration from Vector
   Enumeration enumerate = Barista.elements();
// Traversing to the elements using hasMoreElements() method.
   while(enumerate.hasMoreElements())
   {
    //
     System.out.println(enumerate.nextElement());
   }
 }
}
// Below is the code snippet for Iterator
import java.util.*;
public class EnumerationIteration
{
 public static void main(String args[])
 {
   Vector foodChain = new Vector();
   foodChain.add("Mc Donalds"); foodChain.add("Subway"); foodChain.add("Chipotele");
   System.out.println("USA Food shops:");
   Iterator it = foodChain.iterator();
   while(it.hasNext())
   {
     System.out.println(it.next());
   }
 }
}

Stack and Queue work on the principal of First in Last Out and First in First out.

The best use case of Queue is in the messaging services, where we implement the messaging container, which allows one message to get input and another message get output from the queue.

The queue has the ability to also act as a buffer to store the elements for the temporary time.

Whereas Stack best use is to perform prefix, postfix and infix operation in the Tree.

prefix, postfix and infix operation in the Tree.

Below is the code example of Stack:

import java.util.Stack;
public class StackExample {
   public static void main(String a[]){
       Stack<Integer> stack = new Stack<>();
       System.out.println("Empty stack : "  + stack);
// Checking whether the stack is empty?
       System.out.println("Empty stack : "  + stack.isEmpty());
       // Exception in thread "main" java.util.EmptyStackException
// Adding the elements in the queue
       stack.push(1001);
       stack.push(1002);
       stack.push(1003);
       stack.push(1004);
       System.out.println("Non-Empty stack : "  + stack);
// Getting the elements from the queue
       System.out.println("Non-Empty stack: Pop Operation : "  + stack.pop());
       System.out.println("Non-Empty stack : After Pop Operation : "  + stack);
// Checking whether the elements in the queue.
       System.out.println("Non-Empty stack : search() Operation : "  + stack.search(1002));
       System.out.println("Non-Empty stack : "  + stack.isEmpty());
   }
}

Below is the code example of Queue:

import java.util.LinkedList;
import java.util.Queue;
public class QueueExample
{
 public static void main(String[] args)
 {
   Queue<Integer> queue = new LinkedList<>();
   // Adds elements {0, 1, 2, 3, 4} to queue
   for (int i=0; i<5; i++)
    queue.add(i);
   // Display contents of the queue.
   System.out.println("Elements of queue-"+q);
   // To remove the head of queue.
   int removedele = queue.remove();
   System.out.println("removed element-" + removedele);
   System.out.println(queue);
   // To view the head of queue
   int head = queue.peek();
   System.out.println("head of queue-" + head);
   // Rest all methods of collection interface,
   // Like size and contains can be used with this
   // implementation.
   int size = queue.size();
   System.out.println("Size of queue-" + size);
 }
}

CopyOnWriteArrayList creates a clone of the actual list, for every successive update operation will update internally to the actual list without generating the concurrent modification exception, and prevent to throw the concurrent modification exception. However, to use this cost more on memory consumption because for every update operation there is one clone copy gets created, we have to use wisely when there is frequent read operation, it is better to use when the same list is shared by a number of thread, whereas ArrayList doesn’t allow any thread safety.

However whenever any modification takes place while traversing in the Array lIst its throws the concurrent modification exception.

The concurrentHashMap works on the principle of segmentation, which divides the  HashMap into segments and each segment allocated to each thread and locked for same so other thread will not interfere with each other, the default size of segments in each ConcurrentHashMap is 32.

Below is the internal code structure of ConcurrentHashMap.

This below code works as an internal class of ConcurrentHashMap.

protected static final class Segment {
 protected int count;
// The count is a counter which update is synchronized
 protected synchronized int getCount() {
   return this.count;
 }
 protected synchronized void synch() {}
}
/** Segment Array declaration **/
public final Segment[] segments = new Segment[32];  

It's no surprise that this one pops up often in Java collections programming interview questions.

WeakHashMap is using the WeakReference class, first, we need to understand how WeakReference works, WeakReference is a class which holds the reference and GC captured that reference eagerly and does the garbage collection.

Below is the code snippet for WeakReference.

Integer prime = 1; 
WeakReference<Integer> soft = new WeakReference<Integer>(prime);
prime = null;

In this case, prime no gets garbage collected easily.

The WeakHashMap has utilized the WeakReference classes, below code structure of WeakHashMap shows about the utilizing of WeakReference. The WeakHashMap extends the WeakReference classes

private static class Entry<K,V> extends WeakReference<Object> implements Map.Entry<K,V> {
        V value;
        final int hash;
        Entry<K,V> next;
        /**
         * Creates new entry.
         */
        Entry(Object key, V value,
              ReferenceQueue<Object> queue,
              int hash, Entry<K,V> next) {
            super(key, queue);
            this.value = value;
            this.hash = hash;
            this.next = next;
        }
        @SuppressWarnings("unchecked")
        public K getKey() {
            return (K) WeakHashMap.unmaskNull(get());
        } 

Key is stored as a WeakReference in ReferenceQueue, whenever the JVM get the key its add in the ReferenceQueue, so as soon as WeakHashMap check any key and if it found is deletes it from the table, this operation is called every time before any get operation.

Predominantly the WeakHashMap is given better memory performance, one can use this whenever we have to implement the custom cache or need to add a volume of the record in the Map.

Description

Java interview questions can range from basic to advanced and answering them confidently and accurately can demonstrate a candidate's Java programming skills. It is also important to understand the concepts related to collections and how to effectively implement them in software to improve the quality and performance of code and general applications. In addition, several collection classes are available in Java, such as ch as ArrayList, LinkedList, HashMap, and TreeSet, each with unique properties and implementation. Knowing how to use these frameworks effectively can greatly impact the development process and produce efficient and optimized code. You can opt for this Java course to master these concepts. 

In conclusion, mastery of Java collections is very important for any Java developer, and constant learning and practice are required to stay up to date with new frameworks and technologies. A solid understanding of collections can lead to successful careers in Java programming and job opportunities in a variety of industries, including finance, healthcare, and technology. Therefore, it is very important to go through and understand the Java Collection interview questions to improve your chances of getting a job and succeeding in Java programming. 

Additionally, you may enroll in some of the best programming certifications from knowledgeHut. 

Read More
Levels