Sunday, 12 March 2017

Java Collection Basic Questions Part 3

1) What is the importance of hashCode() and equals() methods? You must override hashCode() in every class that overrides equals(). Fail... thumbnail 1 summary
1) What is the importance of hashCode() and equals() methods?

You must override hashCode() in every class that overrides equals(). Failure to do so will result in a violation of the general contract for Object.hashCode(), which will prevent your class from functioning properly in conjunction with all hash-based collections, including HashMap, HashSet, and Hashtable.
Let's try to understand it with an example of what would happen if we override equals() without overriding hashCode() and attempt to use a Map.
Say we have a class like this and that two objects of MyClass are equal if their importantField is equal (with hashCode() and equals() generated by eclipse)
public class MyClass {

    private final String importantField;
    private final String anotherField;

    public MyClass(final String equalField, final String anotherField) {
        this.importantField = equalField;
        this.anotherField = anotherField;
    }

    public String getEqualField() {
        return importantField;
    }

    public String getAnotherField() {
        return anotherField;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result
                + ((importantField == null) ? 0 : importantField.hashCode());
        return result;
    }

    @Override
    public boolean equals(final Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        final MyClass other = (MyClass) obj;
        if (importantField == null) {
            if (other.importantField != null)
                return false;
        } else if (!importantField.equals(other.importantField))
            return false;
        return true;
    }

}

Override only equals

If only equals is overriden, then when you call myMap.put(first,someValue) first will hash to some bucket and when you call myMap.put(second,someOtherValue) it will hash to some other bucket (as they have a different hashCode). So, although they are equal, as they don't hash to the same bucket, the map can't realize it and both of them stay in the map.

Although it is not necessary to override equals() if we override hashCode(), let's see what would happen in this particular case where we know that two objects of MyClass are equal if their importantField is equal but we do not override equals().

Override only hashCode

Imagine you have this
MyClass first = new MyClass("a","first");
MyClass second = new MyClass("a","second");
If you only override hashCode then when you call myMap.put(first,someValue) it takes first, calculates its hashCode and stores it in a given bucket. Then when you call myMap.put(second,someOtherValue) it should replace first with second as per the Map Documentation because they are equal (according to the business requirement).

But the problem is that equals was not redefined, so when the map hashes second and iterates through the bucket looking if there is an object k such that second.equals(k) is true it won't find any as second.equals(first) will be false.

2) What is the difference between peek(),element() ,poll() and remove() method of the Queue interface ?

The peek() method retrieves the value of the first element of the queue without removing it from the queue. For each invocation of the method we always get the same value and its execution
does not affect the size of the queue. If the queue is empty the peek() method returns null.

The element() method behaves like peek(), so it again retrieves the value of the first element without removing it. Unlike peek ), however, if the list is empty element() throws a NoSuchElementException.

The poll() method retrieves the value of the first element of the queue by removing it from the queue. At each invocation it removes the first element of the list and if the list is already empty it returns null but does not throw any exception.

The remove() method behaves as the poll() method, so it removes the first element of the list and if the list is empty it throws a NoSuchElementException.

3) How to avoid ConcurrentModificationException while iterating a collection?

ConcurrentModificationException can come if two threads trying to modify one list at same time. E.g one thread is iterating over it and other is trying to remove elements from it. But more commonly it comes when you use Array List remove() method while iterating over list.
To avoid this always use iterator's  remove() method to delete elements while traversing.

4) Write java code showing insertion,deletion and retrieval of HashMap object ?

import java.util.HashMap;
import java.util.Map;
import java.util.Iterator;
import java.util.Set;
public class Details {

   public static void main(String args[]) {

      /* This is how to declare HashMap */
      HashMap hmap = new HashMap();

      /*Adding elements to HashMap*/
      hmap.put(12, "Chaitanya");
      hmap.put(2, "Rahul");
      hmap.put(7, "Singh");
      hmap.put(49, "Ajeet");
      hmap.put(3, "Anuj");

      /* Display content using Iterator*/
      Set set = hmap.entrySet();
      Iterator itr1 = set.iterator();
      while(itr1.hasNext()) {
         Map.Entry entry = (Map.Entry)itr1.next();
         System.out.print("key is: "+ entry.getKey() + " & Value is: ");
         System.out.println(entry.getValue());
      }

      /* Get values based on key*/
      String var= hmap.get(2);
      System.out.println("Value at index 2 is: "+var);

      /* Remove values based on key*/
      hmap.remove(3);
      System.out.println("Map key and values after removal:");
      Set set2 = hmap.entrySet();
      Iterator itr = set2.iterator();
      while(itr.hasNext()) {
          Map.Entry entry1 = (Map.Entry)itr.next();
          System.out.print("Key is: "+entry1.getKey() + " & Value is: ");
          System.out.println(entry1.getValue());
       }

   }
}

Output:
key is: 49 & Value is: Ajeet
key is: 2 & Value is: Rahul
key is: 3 & Value is: Anuj
key is: 7 & Value is: Singh
key is: 12 & Value is: Chaitanya
Value at index 2 is: Rahul
Map key and values after removal:
Key is: 49 & Value is: Ajeet
Key is: 2 & Value is: Rahul
Key is: 7 & Value is: Singh
Key is: 12 & Value is: Chaitanya

5) What is UnsupportedOperationException?

UnsupportedOperationException is class which extends RuntimeException and this exception thrown to indicate that requested operation is not supported by API.
Reason is, when call java.util.Arrays.asList(String… a) method it returns fixed-size list backed by specified array so when you try to modify the list i.e. add or remove value from it will throw UnsupportedOperationException.
Below code will throw UnsupportedOperationException:

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

public class UnsupportedException {

 public static void main(String[] args) {

 String[] strings = { "Java", "Honk", "Test" };

 List list = Arrays.asList(strings);

 for (Iterator iterator = 
   list.iterator(); iterator.hasNext();) {
  String string = iterator.next();
  iterator.remove();
   }

 }

}

To fix UnsupportedOperationException use LinkedList constructor and pass collection object as parameter. Please see sample code below :
import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

public class UnsupportedExceptionFix {

 public static void main(String[] args) {

 String[] strings = { "Java", "Honk", "Test" };

 List list2 = 
   new LinkedList(Arrays.asList(strings));
 for (Iterator iterator = 
   list2.iterator(); iterator.hasNext();) {
  String string = iterator.next();
  iterator.remove();
 }
 System.out.println("Removed all data from list");

 }

}

6) What is the difference between HashMap and ConcurrentHashMap ?

Significant difference between HashMap and ConcurrentHashMap is that ConcurrentHashMap is thread-safe and can be used in a concurrent environment without external synchronization. Though it doesn't provide the same level of synchronization as achieved by using Hashtable but it's enough for the most practical purpose.

HashMap can be synchronized by wrapping it on Collections.synchornizedMap(HashMap) which will return a collection which is almost equivalent to Hashtable, where every modification operation on Map is locked on whole Map object while in case of ConcurrentHashMap, thread-safety is achieved by dividing whole Map into different partition based upon Concurrency level and only locking particular portion instead of locking the whole Map.

ConcurrentHashMap is more scalable and performs better than Synchronized HashMap in the multi-threaded environment while in Single threaded environment both HashMap and ConcurrentHashMap gives comparable performance, where HashMap only slightly better.

No comments

Post a Comment