Sunday, 20 October 2019

Difference Between Fail-Fast and Fail-Safe Iterators in Java

       In this post, we will discuss the differences between fail-fast and fail-safe iterators in Java. This is a common Java interview question and is closely related to the ConcurrentModificationException and the situations in which this exception is thrown.

A fail-fast iterator throws a ConcurrentModificationException if the underlying collection is structurally modified while it is being iterated (except through the iterator's own remove() method). In contrast, a fail-safe iterator does not throw this exception because it iterates over a copy (or snapshot) of the collection rather than the original collection.

Now, let's explore the key differences between fail-fast and fail-safe iterators in detail, along with practical examples.


Related topicDifference between HashSet and TreeSet in Java

Difference between fail-fast and fail-safe iterator:-
diff. between fail-fast and fail-safe
Difference between fail-fast and fail-safe iterator

Example of fail-fast iterator:-

        As shown in the diagram, the primary difference between fail-fast and fail-safe iterators lies in how they handle ConcurrentModificationException.

Most Java collections store their elements internally using data structures such as arrays, linked lists, or hash tables. While iterating over a collection, modifying the underlying collection directly is generally not allowed, as it can lead to unpredictable behavior.

Fail-fast iterators internally maintain a modification count (modCount) to detect structural changes to the collection. When an iterator is created, it stores the current value of modCount as the expected modification count. During iteration, the iterator continuously compares the current modCount with the expected value. If they differ, it indicates that the collection has been structurally modified outside the iterator, and a ConcurrentModificationException is thrown.


FailFastIteratorExample.java,
package com.example.demo;

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

public class FailFastIteratorExample {

        public static void main(String[] args) {

                List<String> list = new ArrayList<String>();
                list.add("A");
                list.add("B");

                Iterator<String> iterator = list.iterator();

                while (iterator.hasNext()) {
                        iterator.next();
                        list.add("C");
                }
        }

}

Output:-
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(Unknown Source)
at java.util.ArrayList$Itr.next(Unknown Source)
at com.example.demo.FailFastIteratorExample.main(FailFastIteratorExample.java:17)


Example Of fail-safe iterator:-

            A fail-safe iterator does not throw a ConcurrentModificationException if the collection is modified while it is being iterated. Instead, it operates on a copy (or snapshot) of the original collection rather than the collection itself.

Because the iterator traverses the copied collection, any modifications made to the original collection after the iterator is created do not affect the iteration. As a result, changes to the original collection are not reflected in the current iteration, and no ConcurrentModificationException is thrown.


FailSafeIteratorExample.java,
package com.example.demo;

import java.util.Iterator;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

public class FailSafeIteratorExample {

        public static void main(String[] args) {

               List<String> list = new CopyOnWriteArrayList<String>();
               list.add("A");
               list.add("B");

               Iterator<String> iterator = list.iterator();

               while (iterator.hasNext()) {
                       iterator.next();
                       list.add("C");
               }

               list.forEach(str -> System.out.println(str));

       }

}

Output:-
A
B
C
C

Related Posts:-
1) Collection Hierarchy in Java
2) Collection Interview Questions and Answers in Java(List,Map & Set)
3) How to iterate the TreeMap in reverse order in Java
4) How to Remove duplicates from ArrayList in Java
5) Internal Implementation of TreeMap in Java
6) Internal implementation of ArrayList in Java
7) Internal Implementation of LinkedList in Java
8) Difference between HashSet and TreeSet in Java

Wednesday, 9 October 2019

UnsupportedOperationException When Adding or Removing Elements from a Collection in Java

          In the previous article, we discussed the differences between HashSet and TreeSet and explored practical examples of both. In this article, we will discuss one of the important exceptions in the Java Collections Framework: UnsupportedOperationException.


What is UnsupportedOperationException in collection?

          As the name suggests, UnsupportedOperationException indicates that the requested operation is not supported. It is thrown when you attempt to perform an operation on a collection or object that does not support that operation.

UnsupportedOperationException is thrown at runtime, which means it is a subclass of RuntimeException and therefore an unchecked exception.


Throwable -> Exception -> RuntimeException -> UnsupportedOperationException


Scenarios That Throw UnsupportedOperationException:-

1) Update on list of Arrays.asList() 

      As we know, the asList() method of the Arrays (java.util.Arrays) class is used to convert an array into a List.

However, the list returned by Arrays.asList() is a fixed-size list backed by the original array. If you try to add or remove elements from this list, Java throws an UnsupportedOperationException, as demonstrated in the example below.


UnsupportedOpExceptionExample.java,
package com.example.demo;

import java.util.Arrays;
import java.util.List;

public class UnsupportedOpExceptionExample {
     
     public static void main(String[] args) {
          
           String[] arrayStr = { "Mechanical", "Computer Science" };
           List<String> list = Arrays.asList(arrayStr);
           list.add("ECE");
     }
}

Output:-
Exception in thread "main" java.lang.UnsupportedOperationException
at java.util.AbstractList.add(Unknown Source)
at java.util.AbstractList.add(Unknown Source)
at com.example.demo.UnsupportedOpExceptionExample.main(UnsupportedOpExceptionExample.java:12)



Solution:-

Pass Arrays.asList to the List constructor, so that will act as new ArrayList object, then you can modify the list without any exception.

UnsupportedOpExceptionExample.java
package com.example.demo;

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

public class UnsupportedOpExceptionExample {

       public static void main(String[] args) {

              String[] arrayStr = { "Mechanical", "Computer Science" };
              List<String> list = new ArrayList<String>(Arrays.asList(arrayStr));
              list.add("ECE");

              list.stream().forEach(str -> System.out.println(str));
       }
}
Output:-
Mechanical
Computer Science
ECE



2) Update on Unmodifiable collection

           You can create an unmodifiable collection by using the Collections.unmodifiableCollection(Collection<? extends E> c) method or one of its related methods, such as Collections.unmodifiableList(), Collections.unmodifiableSet(), or Collections.unmodifiableMap().

An unmodifiable collection provides read-only access to its elements. You can read or iterate over the collection, but you cannot perform structural modifications such as adding, removing, or updating elements through the unmodifiable view. If you attempt to modify an unmodifiable collection, Java throws an UnsupportedOperationException, which is an unchecked exception (RuntimeException).

The following example demonstrates how an UnsupportedOperationException is thrown when attempting to modify an unmodifiable collection.


UnsupportedOpExceptionExample.java
package com.example.demo;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

public class UnsupportedOpExceptionExample {

        public static void main(String[] args) {

               List<String> list = new ArrayList<String>();
               list.add("Mechanical");
               list.add("ECE");
               Collection<String> strList = Collections.unmodifiableCollection(list);
               strList.add("CS");

               list.stream().forEach(str -> System.out.println(str));
        }

}

Output:-
Exception in thread "main" java.lang.UnsupportedOperationException
at java.util.Collections$UnmodifiableCollection.add(Unknown Source) at com.example.demo.UnsupportedOpExceptionExample.main(UnsupportedOpExceptionExample.java:16)


  Use an unmodifiable collection only when the collection is intended to be read-only and no modifications are required. If you need to add, remove, or update elements, avoid using an unmodifiable collection.

Thank you for visiting the blog!..

Sunday, 6 October 2019

Why Does TreeSet Not Allow null Values in Java?

            TreeSet implements the Set interface and stores only unique elements, which means it does not allow duplicate values. By default, the elements in a TreeSet are stored in their natural (ascending) order. In this post, we will discuss why TreeSet does not allow null values.

Example: Adding a null Value to a TreeSet

TreeSetExample.java

package com.example.demo;

import java.util.TreeSet;

public class TreeSetExample {

         public static void main(String[] args) {

                 TreeSet<String> treeSet = new TreeSet<String>();
                  treeSet.add(null);

         }

}

Output:-
Exception in thread "main" java.lang.NullPointerException
at java.util.TreeMap.compare(Unknown Source)
at java.util.TreeMap.put(Unknown Source)
at java.util.TreeSet.add(Unknown Source)
at com.example.demo.TreeSetExample.main(TreeSetExample.java:10)


Why Doesn't TreeSet Allow null Values?

TreeSet stores its elements in sorted order. By default, it uses the natural ordering of elements, which relies on the Comparable interface and its compareTo() method. The compareTo() method compares one object with another to determine their ordering.

Since null is not an object and does not have a compareTo() method, TreeSet cannot compare a null value with other elements. As a result, attempting to add a null value to a TreeSet (using natural ordering) results in a NullPointerException.

Method declaration:

public boolean add(E e) throws ClassCastException, NullPointerException;

      In Java 6 and earlier, a TreeSet could accept null as the first element because no comparison was required when the set was empty. However, this behavior was changed in Java 7. From Java 7 onward, TreeSet does not allow null values at all when using natural ordering, and attempting to add one throws a NullPointerException.

Thank you for visiting the blog.

Saturday, 5 October 2019

Difference Between HashSet and TreeSet in Java

        In this post, we will discuss the differences between HashSet and TreeSet in Java. Both HashSet and TreeSet implement the Set interface and do not allow duplicate elements. However, they differ in terms of ordering, performance, and internal implementation.

Difference between HashSet and TreeSet:-

  1. Performance: HashSet provides better performance than TreeSet for operations such as add(), remove(), contains(), and size(). HashSet offers an average time complexity of O(1) for these operations, whereas TreeSet provides O(log n) time complexity.

  2. Internal Implementation: HashSet is internally backed by a HashMap, whereas TreeSet is backed by a TreeMap. TreeMap uses a Red-Black Tree to store elements in sorted order.

  3. Ordering: HashSet does not maintain any order of its elements. In contrast, TreeSet stores elements in their natural (ascending) order by default. Both HashSet and TreeSet do not allow duplicate elements.

  4. Null Values: HashSet allows one null element, whereas TreeSet does not allow null elements (when using natural ordering). Attempting to add null to a TreeSet results in a NullPointerException because it internally uses the compareTo() method (or a Comparator) to compare elements.

Use a TreeSet when you need the elements to be stored in sorted order. If ordering is not required and performance is the primary concern, HashSet is the better choice.

HashSet and TreeSet examples:--

HashSetTreeSetExample.java
package com.example.demo;

import java.util.HashSet;
import java.util.TreeSet;

public class HashSetTreeSetExample {

       public static void main(String[] args) {

               HashSet<String> hashSet = new HashSet<String>();
               hashSet.add("Java");
               hashSet.add(".Net");
               hashSet.add("PHP");
               hashSet.add("Embedded C");

               System.out.println("HashSet elements are,");
               hashSet.stream().forEach(System.out::println);

               TreeSet<String> treeSet = new TreeSet<String>();
               treeSet.add("Java");
               treeSet.add(".Net");
               treeSet.add("PHP");
               treeSet.add("Embedded C");

               System.out.println("TreeSet elements are,");
               treeSet.stream().forEach(System.out::println);
       }
}

Output:--
HashSet elements are,
Java
Embedded C
.Net
PHP

TreeSet elements are,
.Net
Embedded C
Java
PHP


Thank you for visiting the blog.

Thursday, 3 October 2019

Java Collection Framework Hierarchy

            A Collection is a group of objects represented as a single unit. The Java Collection Framework provides several interfaces and classes to store and manipulate groups of objects efficiently.

The Collection Framework is one of the most important topics for Java interviews. You should understand the usage, differences, and advantages of its various interfaces and classes.

In this post, we will discuss the Collection Framework hierarchy.

All the classes and interfaces related to the Java Collection Framework are available in the java.util package. The Collection interface is the root interface of the Collection Framework hierarchy, as shown in the diagram below.

The following diagram illustrates the class hierarchy of the Java Collection Framework.

Collection Hierarchy in Java
Collection Hierarchy in Java

        The Java Collection Framework is divided into four main interfaces. Three of them—List, Set, and Queue—extend the Collection interface, whereas the Map interface does not extend the Collection interface. Instead, it is a separate interface in the Java Collections Framework.

  •  List 
      The List interface is implemented by the ArrayList, Vector, and LinkedList classes. It allows duplicate elements and preserves the insertion order of elements.

  • Queue
     The Queue interface represents a collection of elements that are typically processed in a First-In, First-Out (FIFO) order. In a queue, elements are generally added at the tail and removed from the head. The LinkedList and PriorityQueue classes implement the Queue interface.

  • Set
      The HashSet and LinkedHashSet classes implement the Set interface, while the TreeSet class implements the SortedSet interface, which in turn extends the Set interface. A Set does not allow duplicate elements.

  • Map
       The Map interface is the only interface in the Java Collections Framework that does not extend the Collection interface. It stores data as key-value pairs, where each key is unique and maps to a corresponding value. The HashMap and Hashtable classes implement the Map interface, while the TreeMap class implements the SortedMap interface, which in turn extends the Map interface.

Summary

  • Collection is the root interface for collections of individual objects.
  • List stores ordered elements and allows duplicates.
  • Set stores unique elements.
  • Queue is used for processing elements, typically in FIFO order.
  • Map stores key-value pairs and is independent of the Collection interface.
  • The framework provides multiple implementations, each optimized for different use cases, making it easier to choose the right data structure for your application.