Top 50 Java Collections Interview Questions You Must Prepare 19.Mar.2024

Collections.sort() method will sort the ArrayList of String in ascending order. 

To have a descending order either comparator has to be provided or reverseOrder method of the Collections class can be used.

Collections.sort(cityList, Collections.reverseOrder());

Though both HashTable and HashMap store elements as a (key, value) pair and use hashing technique to store elements, moreover from Java v1.2, HashTable class was retrofitted to implement the Map interface, making it a member of the Java Collections Framework. But there are certain difference between the two -

  • HashMap is not synchronized where as HashTable is synchronized.
  • HashMap allows one null value as a key and any number of null values where as HashTable does not allow null values either as key or as value.
  • For traversing a HashMap an iterator can be used. For traversing a HashTable either an iterator or Enumerator can be used. The iterator used for both HashMap and HashTable is fail-fast but the enumerator used with HashTable is fail-safe.
  • Performance wise HashMap is faster than the HashTable reason being HashMap is not synchronized.

Basic data structure used by ArrayList to store objects is an array of Object class, which is defined like -

trient Object[] elementData; 

When we add an element to an ArrayList it first verifies whether it has that much capacity in the array to store new element or not, in case there is not then the new capacity is calculated which is 50% more than the old capacity and the array is increased by that much capacity (Actually uses Arrays.copyOf which returns the original array increased to the new length)

LinkedList class in Java implements List and Deque interfaces and LinkedList implements it using doubly linkedlist.

Within the LinkedList implementation there is a private class Node which provides the structure for a node in a doubly linked list. It has item variable for holding the value and two reference to Node class itself for connecting to next and previous nodes.

Java Collections Framework provides a standard way to handle a group of objects. Benefits of the Collections Framework are -

  • High performance, implementation of the Collection classes (ArrayList, LinkedList, HashSet etc.) are very efficient and used as-is in most of the cases.
  • Reduced effort as framework already provides several implementations for most of the scenarios that can be used as-is.
  • Provides interoperability, as exp. if you are using List interface any implementation that implements List interface can be swapped with the existing one.
  • If you want to extend any collection that can be easily done using the standard interfaces provided with in the Collections frameworks.

Some of the Collection classes that implement List interface are ArrayList, CopyOnWriteArrayList, LinkedList, Stack, Vector.

  • List provides a method add(E e) which appends specified element to the end of the list. Using add(E e) method will mean keep adding elements sequentially to the list.
  • Another add method - add(int index, E element) inserts the specified element at the specified position in this list.
  • Third method addAll(int index, Collection<? extends E> c) inserts all of the elements in the specified collection into this list, starting at the specified position.

Another feature which was added to Collection with JDK 5 is for-each style loop. Any collection which wants to be the target of the "for-each loop" statement has to implement iterable interface.

Using for-each loop is easier than constructing the iterator to iterate over the collection and can be used in most of the cases rather than using the iterator loop.

If you have a list of Strings, that can be iterated using for-each loop like this.

for(String city : cityList){
    System.out.println("Name " + city);
}

At the root of the Collections framework is Collection interface, it must be implemented by any class that defines a collection. This interface declares the core methods that every collection will have, if any class doesn't implement any of the method then it can throw UnsupportedOperationException.

Then there are List and Set interfaces that extend Collection interface and provided some of its own behaviour that will be further implemented by the classes that implement List and Set interfaces respectively. 

There is also a Queue interface that extends collection to provide behaviour of a queue. On the other hand there is Map interface which provides core methods for the Map implementations.

Iterator can be obtained on any Collection class like List or Set so contract for Iterator makes no guarantees about the order of iteration. 

But ListIterator can only be used to traverse a List so it does guarantee the order of the iteration. That is why ListIterator provides an add operation.

ListIterator provides the functionality to iterate a list in both directions. The interesting point about list iterator is that it has no current element. Its current cursor position always lies between the element that would be returned by a call to previous() and the element that would be returned by a call to next().

In HashMap, using the key, a Hash is calculated and that hash value decides in which bucket the particular Map.Entry object will reside.

Hash collision me more than one key having the same calculated hash value thus stored in the same bucket. In HashMap, in that case Entry objects are stored as a linked-list with in the same bucket.

The default implementation of equals() method in the Object class is a simple reference equality check.

public boolean equals(Object obj){
     return (this == obj);
}

The default implementation of hashCode() in the Object class just returns integer value of the memory address of the object.

It becomes very important to override these two methods in case we are using a custom object as key in a hash based collection. 

In that case we can't rely on the default implementation provided by the Object class and need to provide custom implementation of hashCode() and equals() method.

If two objects Obj1 and Obj2 are equal according to their equals() method then they must have the same hash code too. Though the vice-versa is not true that is if two objects have the same hash code then they do not have to be equal too.

In Java collections framework ArrayList and LinkedList are two different implementations of List interface 

  • LinkedList is implemented using a doubly linked list concept where as ArrayList internally uses an array of Objects which can be resized dynamically
  • For LinkedList add(e Element) is always O(1) where as for ArrayList add(e Element) operation runs in amortized constant time, that is, adding n elements requires O(n) time.
  • For LinkedList get(int index) is O(n) where as for ArrayList get(int index) is O(1).
  • If you are removing using the remove(int index) method then for LinkedList class it will be O(n). In case of ArrayList getting to that index is fast but removing will mean shuffling the remaining elements to fill the gap created by the removed element with in the underlying array.

With JDK5 the Collection framework was reengineered to add generics. That was done to add "Type Safety" to the collections. Prior to JDK5 (and generics) elements were stored in collections as Object references, which brought the danger of accidentally storing incompatible types in Collection because for Collections everything was Object reference. Which would have resulted in run time error when trying to retrieve elements and casting them to the desired type. 

With the introduction of generics now we can explicitly tell which data type is to be stored in the collections, which helps in avoiding the run time error. As Collection will throw error at compile time itself for incompatible data type. As exp.

Map<String, String> cityTemperatureMap = new LinkedHashMap<String, String>();

Here we are explicitly stating that this LinkedHashMap can only store string as both key and value.

WeakHashMap is a Hash table based implementation of the Map interface, with weak keys. An entry in a WeakHashMap will automatically be removed when its key is no longer in ordinary use. Which me storing only weak references allows garbage collector to remove the entry (key-value pair) from the map when its key is not referenced outside of the WeakHashMap.

In WeakHashMap both null values and the null key are supported. A WeakHashMap is created as- 

Map weakHashMap = new WeakHashMap();

  • Iterator can be obtained on any Collection class like List or Set. But ListIterator can only be used to traverse a List.
  • Iterator only moves in one direction using next() method. ListIterator can iterate in both directions using next() and previous() methods.
  • Iterator always start at the beginning of the collection.

    ListIterator can be obtained at any point.

    As Exp. If you have a List of integers numberList, you can obtain a ListIterator from the third index of this List.

ListIterator<Integer> ltr = numberList.listIterator(3);

  • ListIterator provides an add(E e) method which is not there in Iterator. add(E e) inserts the specified element into the list.
  • ListItearator also provides set method. void set(E e) replaces the last element returned by next() or previous() with the specified element

Diamond operator let the compiler infer the type arguments for the generic classes. It is added in Java 7. 

As Example - Before JDK7 if we had to define a Map using String as both Key and Value we had to write it like this -

Map<String, String> cityMap = new HashMap<String, String>();

In Java SE 7, you can substitute the parameterized type of the constructor with an empty set of type parameters (<>) known as diamond operator.

Map<String, String> cityMap = new HashMap<>();

List provides a method addAll to join two or more lists in Java. 

If you have one list cityList and another List secondCityList then you can join them using addAll like this - 

cityList.addAll(secondCityList);

In ArrayList any number of nulls can be added.

Collections class provides static method to make a Collection unmodifiable. Each of the six core collection interfaces -Collection, Set, List, Map, SortedSet, and SortedMap - has one static factory method.

public static <T> Collection<T> unmodifiableCollection(Collection<? extends T> c);

public static <T> Set<T> unmodifiableSet(Set<? extends T> s);

public static <T> List<T> unmodifiableList(List<? extends T> list);

public static <K,V> Map<K, V> unmodifiableMap(Map<? extends K, ? extends V> m);

public static <T> SortedSet<T> unmodifiableSortedSet(SortedSet<? extends T> s);

public static <K,V> SortedMap<K, V> unmodifiableSortedMap(SortedMap<K, ? extends V> m);

ArrayList provides several methods to remove elements from the List. Since ArrayList internally uses array to store elements, one point to note is that when an element is removed from the List the remaining elements have to be shifted to fill the gap created in the underlying array.

  • clear() - Removes all of the elements from this list.
  • remove(int index) - Removes the element at the specified position in this list.
  • remove(Object o) - Removes the first occurrence of the specified element from this list, if it is present.
  • removeAll(Collection<?> c) - Removes from this list all of its elements that are contained in the specified collection.
  • removeIf(Predicate<? super E> filter) - Removes all of the elements of this collection that satisfy the given predicate.

We can use a HashSet to do the job of removing the duplicate elements. HashSet only stores unique elements and we'll use that feature of HashSet to remove duplicates.

If you have a List called cityList you can create a HashSet using this list - 

Set<String> citySet = new HashSet<String>(cityList);
then add this set back to the cityList 
cityList.clear();
cityList.addAll(citySet);

That will remove all the duplicate elements from the given list. Note that insertion order won't be retained if a HashSet is used. In case insertion order is to be retained use LinkedHashSet.

In the initial Collection classes like Vector, HashTable and Stack all the methods were synchronized to make these classes thread safe. 

Later implementations starting from Java 1.2 were not synchronized. 

Classes in java.util.concurrent package like ConcurrentHashMap, CopyOnWriteArrayList also provides thread safety but with a different implementation that doesn't require making all the methods synchronized.

In the case of ArrayList you don't have to anticipate in advance, like in the case of array, how many elements you are going to store in the arraylist. As and when elements are added list keeps growing. That is why ArrayList is called dynamically growing array.

For ArrayList, data structure used for storing elements is array itself. When ArrayList is created it initializes an array with an initial capacity (default is array of length 10). When that limit is crossed another array is created which is 1.5 times the original array and the elements from the old array are copied to the new array.

In Java.util package the classes that implement random access interface are - ArrayList, CopyOnWriteArrayList, Stack, Vector.

If an attempt is made to add the same key twice, it won't cause any error but the value which is added later will override the previous value. 

As Exp. If you have a Map, cityMap and you add two values with same key in the cityMap, Kolkata will override the New Delhi.

cityMap.put("5", "New Delhi");
cityMap.put("5", "Kolkata");

Java 8 has added a functional style lopping to the Collections. Real advantage of this loop is when it is used on a stream with the chain of functional methods. If you have a Map<String, String> then it can be looped using ForEach statement like this - 

Set<Map.Entry<String, String>> valueSet = cityMap.entrySet();

valueSet.forEach((a)->System.out.println("Key is " + a.getKey() + " Value is " + a.getValue()));

List interface extends the root interface Collection and adds behaviour for the collection that stores a sequence of elements. These elements can be inserted or accessed by their position in the list using a zero-based index.

Collections class has several static methods that return synchronized collections. Each of the six core collection interfaces -Collection, Set, List, Map, SortedSet, and SortedMap - has one static factory method.

public static <T> Collection<T> synchronizedCollection(Collection<T> c);
public static <T> Set<T> synchronizedSet(Set<T> s);
public static <T> List<T> synchronizedList(List<T> list);
public static <K,V> Map<K,V> synchronizedMap(Map<K,V> m);
public static <T> SortedSet<T> synchronizedSortedSet(SortedSet<T> s);
public static <K,V> SortedMap<K,V> synchronizedSortedMap(SortedMap<K,V> m); 

Each of these methods returns a synchronized (thread-safe) Collection backed up by the specified collection.

LinkedHashSet is also one of the implementation of the Set interface. Actually LinkedHashSet class extends the HashSet and has no other methods of its own.

LinkedHashSet also stores unique elements just like other implemetations of the Set interface. How LinkedHashSet differs is that it maintains the insertion-order; that is elements in the LinkedHashSet are stored in the sequence in which they are inserted. Note that insertion order is not affected if an element is re-inserted into the set.

LinkedHashMap is also one of the implementation of the Map interface, apart from implementing Map interface LinkedHashMap also extends the HashMap class. So just like HashMap, LinkedHashMap also allows one null key and multiple null values.

How it differs from other implementations of the Map interface like HashMap and TreeMap is thatLinkedHashMap maintains the insertion order of the elements which me if we iterate a LinkedHashMap we'll get the keys in the order in which they were inserted in the Map. 

LinkedHashMap maintains a doubly-linked list running through all of its entries and that's how it maintains the iteration order.

  • HashMap makes no guarantees as to the order of the map.

    LinkedHashMap maintains the insertion order of the elements which me if we iterate a LinkedHashMap we'll get the keys in the order in which they were inserted in the Map.

    TreeMap stores objects in sorted order.

  • HashMap as well as LinkedHashMap allows one null as key, multiple values may be null though.

    TreeMap does not allow null as key.

  • HashMap stores elements in a bucket which actually is an index of the array.

    LinkedHashMap also uses the same internal implementation, it also maintains a doubly-linked list running through all of its entries.

    TreeMap is a Red-Black tree based NavigableMap implementation.

  • Performance wise HashMap provides constant time performance O(1) for get() and put() method.

    LinkedHashMap also provides constant time performance O(1) for get() and put() method but in general a little slower than the HashMap as it has to maintain a doubly linked list.

    TreeMap provides guaranteed log(n) time cost for the containsKey, get, put and remove operations.

Map.Entry is an interface in java.util, in fact Entry is a nested interface in Map interface. Nested interface must be qualified by the name of the class or interface of which it is a member, that's why it is qualified as Map.Entry. 

Map.Entry represents a key/value pair that forms one element of a Map. 

The Map.entrySet method returns a collection-view of the map, whose elements are of this class.

As exp. If you have a Map called cityMap then using entrySet method you can get the set view of the map whose elements are of type Map.Entry, and then using getKey and getValue methods of the Map.Entry you can get the key and value pair of the Map.

for(Map.Entry<String, String> entry:  cityMap.entrySet()){

   System.out.println("Key is " + entry.getKey() + " Value is " + entry.getValue());

}

  • ArrayList is not synchronized whereas Vector is synchronized.
  • Performance wise ArrayList is fast in comparison to Vector as ArrayList is not synchronized.
  • ArrayList, by default, grows by 50% in case the initial capacity is exhausted. In case of Vector the backing array's size is doubled by default.
  • For traversing an ArrayList iterator is used. For traversing Vecor Iterator/Enumerator can be used. Note that Iterator is fail-fast for both Vector and ArrayList. Enumerator which can be used with Vector is not fail-fast.

Collections class has a static method sort which can be used for sorting an arraylist.

There are two overloaded versions of sort method -

  • public static <T extends Comparable<? super T>> void sort(List<T> list) - Sorts the specified list into ascending order, according to the natural ordering of its elements.
  • public static <T> void sort(List<T> list, Comparator<? super T> c) - Sorts the specified list according to the order induced by the specified comparator.

If you have a List called cityList it can be sorted like this - 

Collections.sort(cityList);

  • fail-fast iterator throws a ConcurrentModificationException if the underlying collection is structurally modified whereas fail-safe iterator doesn't throw ConcurrentModificationException.
  • fail-fast iterator doesn't create a copy of the collection whereas fail-safe iterator makes a copy of the underlying structure and iteration is done over that snapshot.
  • fail-fast iterator provides operations like remove, add and set (in case of ListIterator) whereas in case of fail-safe iterators element-changing operations on iterators themselves (remove, set and add) are not supported. These methods throw UnsupportedOperationException.

An iterator is considered fail-safe if it does not throw ConcurrentModificationException. ConcurrentModificationException is not thrown as the fail-safe iterator makes a copy of the underlying structure and iteration is done over that snapshot. 

Since iteration is done over a copy of the collection so interference is impossible and the iterator is guaranteed not to throw ConcurrentModificationException.

IdentityHashMap class implements the Map interface with a hash table, using reference-equality in place of object-equality when comparing keys (and values). In other words, in an IdentityHashMap, two keys k1 and k2 are considered equal if and only if (k1==k2). Where as In normal Map implementations (like HashMap) two keys k1 and k2 are considered equal if and only if (k1==null ? k2==null : k1.equals(k2)).

Note that This class is not a general-purpose Map implementation. While this class implements the Map interface, it intentionally violates Map's general contract, which mandates the use of the equals method when comparing objects. This class is designed for use only in the rare cases wherein reference-equality semantics are required.

Arrays class contains a static factory method asList() that allows arrays to be viewed as lists.

public static <T> List<T> asList(T... a)

As Exp.

String cityArray[] = {"Delhi", "Mumbai", "Bangalore", "Hyderabad", "Chennai"};

//Converting array to List

List<String> cityList = Arrays.asList(cityArray);

hashCode() method is present in the java.lang.Object class. This method is used to get a unique integer value for a given object. We can see it's use with hash based collections like HashTable or HashMap where hashCode() is used to find the correct bucket location where the particular (key, value) pair is stored.

In order to have better performance most of the Collections are not synchronized. Which me sharing an instance of arrayList among many threads where those threads are modifying (by adding or removing the values) the collection may result in unpredictable behaviour. 

To synchronize a List Collections.synchronizedList() method can be used.

RandomAccess interface is a marker interface used by List implementations to indicate that they support fast (generally constant time) random access. 

Though we can't iterate a Map as such but Map interface has methods which provide a set view of the Map. That set can be iterated. Those two methods are -

  • Set<Map.Entry<K, V>>entrySet() - This method returns a set that contains the entries in the map. The entries in the set are actually object of type Map.Entry.
  • Set<K>keySet() - This method returns a set that contains the keys of the map.

There is also a values() method in the Map that returns a Collection view of the values contained in this map.

There are many ways to loop/iterate an arrayList in Java. Options are -

  • for loop
  • for-each loop
  • iterator
  • list iterator
  • Java 8 forEach loop

for-each loop is the best way to iterate a list if you just need to traverse sequentially through a list.

Maps themselves are not collections because they don't implement Collection interface.

HashMap allows one null key and any number of null values. If another null key is added it won't cause any error. But the value with the new null key will override the value with old null key.

As exp. If you have a Map, cityMap and you add two values with null keys in the cityMap, Kolkata will override the New Delhi as only one null key is allowed.

cityMap.put(null, "New Delhi");
cityMap.put(null, "Kolkata");

  • Array is fixed in size which is provided at the time of creating an Array. ArrayList grows dynamically and also known as dynamically growing array.
  • Array can store primitive types as well as objects. In ArrayList only Objects can be stored.
  • Arrays can be multi-dimensional whereas ArrayList is always unidimensional.

    As Exp. a two dimensional array can be created as -

    Integer myArray[][] = new Integer[4][3];

  • Performance wise both Array and ArrayList are almost same as internally ArrayList also uses an Array. But there is overhead of resizing the array and copying the elements to the new Array in case of ArrayList.