Friday, January 2, 2015

Multi Threading Part 2

Executing multiple tasks simultaneously is called "Multitasking".

2 types of multitasking:
1. process-based multitasking
2. thread-based multitasking

Process-based multitasking:

Executing several tasks simultaneously. Each task is a separate independent process. 

Example: While writing a Java program in the editor, you can listen to audio songs by MP3 player in the system. At the same time, you can as well download a file from net. All these tasks are executing simultaneously & independent of each other. These all are different processes, hence process-based multitasking.

Thread-based multitasking:

Executing several tasks simultaneously. Each task is an independent part of the same program and is called thread.

Thread-based multitasking is best suited at programmatic level. 
Process-based multitasking is best suited for OS level.

The main objective of multitasking is to improve performance of the system by reducing response time.

Java provides rich built-in API support for multi-threading.

2 ways to define a thread:
  • Extending Thread class
  • Implementing Runnable interface
Extending Thread class Example:

class Mythread extends Thread {
    public void run() {
        for(int i = 0; i < 10; i++) {
            System.out.println("Child Thread");
        }
    }
}
public class ExtendThredClassExample {
    public static void main(String[] args) {
        Mythread t = new Mythread();
        t.start();
        for(int i= 0; i < 10; i++) {
            System.out.println("Main Thread");
        }
    }
}


Thread scheduler:

Whenever multiple threads are waiting to get chance for execution, which thread will get chance first is decided by thread scheduler, whose behavior is JVM and vendor dependent. So, we can't predict exact execution order or exact output.

Thread scheduler is part of JVM.

Difference between t.start() and t.run() ????

In the case of t.start(), a new thread is created and that is responsible for executing run() method.

In the case of t.run(), no new thread will be created. It is equal to calling a class method using an object.

Importance of start() method???

To start a thread, the required mandatory activities like registering the thread with thread scheduler will be automatically done by start() method.

Programmer just have to focus on thread job by overriding run() method. Without start() method, there is no chance of starting a new thread.

What if, we are not overriding run() method????

If you are not overriding run() method, then thread class run() method will be executed which is empty implementation.

It is highly recommended to override run() method of Thread class to provide your thread job.

What if, we are overloading run()???

Overloading run() method is possible, but Thread class start() will only call no argument run() only. The overloaded run() method, can be called just like a normal method.

What if, we are overriding start()???

If we override start() method, then start() will be executed just like a normal method call and no new thread will be created.

Override start method and call super class start() :

class Mythread1 extends Thread {
    public void start() {
        super.start();
        System.out.println("Override start method");
    }
    public void run() {
        System.out.println("run method of thread");
    }
}

public class OverrideStartMethodOfThreadClass {
    public static void main(String[] args) {
        Mythread1 t = new Mythread1();
        t.start();
        System.out.println("Main thread");
    }   
}


This will give o/p as:

Override start method
Main thread
run method of thread


Life cycle of thread:

Mythread t = new Mythread();  --> New or Born state

t.start(); --> Ready/Runnable state

If thread scheduler allocates CPU --> Running state, executing run() method

run() method completes --> Dead state

What if, calling t.start() again???

After starting a starting a thread, we are not allowed to restart the same thread once again, otherwise we will get IllegalThreadStateException.

Mythread t = new Mythread();
t.start(); 
.
.
.
t.start(); // calling again, causes IllegalThreadStateException

Good practice: Never override start() method, but always override run() method.

Defining Thread implementing Runnable interface :

class MyRunnable implements Runnable {
    public void run() {
        for(int i = 0; i < 10; i++) {
            System.out.println("Child Thread");
        }
    }
}
public class DefineThreadImplementingRunnable {
    public static void main(String[] args) {
        MyRunnable r = new MyRunnable();
        Thread t = new Thread(r);
        t.start(); // this will create new thread

        // t.run(); // just calls the Runnable run() method, no new thread will be created
        for(int i= 0; i < 10; i++) {
            System.out.println("Main Thread");
        }
    }
}


Best method:

Define a thread by implementing Runnable interface.

If you define a thread by extending Thread class, you cannot extend any other class. But using Runnable interface, you can extend some other class, while getting the thread functionality. So, using Runnable interface to define a thread is better approach.

Naming of a thread:

Every thread in java has some name, either given by programmer or default by JVM.

We can get and set name of a thread:
  • public final String getName();
  • public final void setName(String name);
public class GetSetThreadNameExample {
    public static void main(String[] args) {
        System.out.println("Current Thread Name: " + Thread.currentThread().getName());
        Thread.currentThread().setName("raghu");
        System.out.println("New name for thread : " + Thread.currentThread().getName());
    }
}


We can get current executing thread reference, by using the following method:

Thread.currentThread()

Thread pirority :

1 to 10  ( 1 is least and 10 is highest )

Thread.MIN_PRIORITY - 1
Thread.NORM_PRIORITY - 5
Thread.MAX_PRIORITY - 10

Thread scheduler will use these priorities while allocating CPU to the threads.

Default priority: only for the main thread is 5. 
But for all other threads, it will be inheriting from the parent. 
  • public final int getPriority();
  • public final void setPriority(int p);
Mythread t = new Mythread();
t.setPriority(10);
t.start();

The methods to prevent thread Execution????

yield() method :

yield() - pause current thread, give chance to waiting threads of same priority

yield() method causes to pause current executing thread for giving the chance to remaining waiting threads of same priority

If there are no waiting threads, or all waiting threads have low priority then the same thread will continue its execution once again.

The thread which is yielded, when it will get chance again is dependent on thread scheduler.

Join() method : 

If a thread wants to wait until completing some other threads, we should go for join().

Example: Marriage event

Venue fixing ( t1) 
Cards printing (t2) --> this has to call t1.join() because unless venue fixing is completed, we can not execute cards printing.
Cards distributing (t3) --> t2.join(). You cannot distribute until you complete cards printing.

join() method is overloaded to specify timeout.

Every join() method throws InterruptedException. Hence, whenever we are using join() method, we should handle InterruptedException either by try-catch or by throws.

class Mythread extends Thread {
    public void run() {
        for(int i = 0; i < 10; i++) {
            System.out.println("Child Thread");
        }
    }
}
public class ExtendThredClassExample {
    public static void main(String[] args) throws InterruptedException {
        Mythread t = new Mythread();
        t.start(); // this will create new thread       
        t.join(); // main thread will not execute until child thread completes

        Thread.sleep(5000);  // this is in milli seconds
        for(int i= 0; i < 10; i++) {
            System.out.println("Main Thread");
        }
    }
}


Sleep() method : 

If a thread don't want to perform any operation for a particular period of time(just pausing), then we should go for sleep.

Whenever we are using sleep() method, we should compulsorily handle InterruptedException other wise we will get compile error.

Thread.sleep(5000); // the time out is in millis

Interruption of a thread:

A thread can interrupt another sleeping or waiting thread. For this, you can use interrupt() method.

You need to catch InterruptException to handle this.

Whenever we are calling interrupt() method, if the target thread is not in sleeping or waiting state, then there is no impact immediately. Interrupt call will wait until target thread entered into sleeping or waiting state. Once target thread entered into sleeping or waiting state, the interrupt call will impact the target thread.



Synchronized modifier : 

synchronized modifier is applicable only for methods and blocks. You cannot use this for classes and variables.

this modifier makes a method or block to allow only one thread at a time to execute.

We can resolve data-inconsistency problem, but it increases waiting time of the threads and effects performance of the system.

Every object in Java has a unique lock associated with it. 'synchronized' modifier is internally implemented using this lock. Using synchronized keyword on a method or block uses this lock.

lock concept is based on object. So, if a thread is executing a synchronized method on a given object, the remaining threads are not allowed to execute any synchronized method on the same object simultaneously. Because, lock is associated with given object.

Whenever multiple threads are operating on same object then only synchronization play the role. If multiple threads are operating on multiple objects, there is no impact of synchronization.

Every class in Java has unique class level lock. 
If a thread wants to execute a static synchronized method, it requires class level lock.

While a thread is executing a static synchronized method, then remaining threads are not allowed to execute any other static synchronized methods of that class simultaneously. 

There is no link between class level lock and object level lock. Both are different.

Synchronized block : 

If very few lines of code requires synchronization, then it is never recommended to declare entire method as synchronized.

main advantage is, it reduces waiting time of the threads and improve performance.

1. We can declare synchronized block to get current object lock as:
            synchronized(this) {  .... }  --> current object level lock

2. To get lock of particular object, say b, declare as :

            synchronized(b) { ...... }  --> get lock of object b

3. To get class level lock, declare as: 
            synchronized(classname.class) { ....... } --> class level lock

A thread can acquire more than one lock at a time.


Inter-thread communication???

Two threads will communicate with each other by using waith(), notify(), notifyAll() methods.

The thread which requires updation will call wait().
The thread which is responsible to update the other thread has to call notify().

If a thread wants to call, waith(), notify(), notifyAll() compulsory the thread should be the owner of that object. That means, the thread has to get lock of that object. This indicates, thread should be in the synchronized area.

Tip: We should call wait(), notify(), notifyAll() methods only from synchronized area otherwise we will get runtime exception saying IllegalMonitorStateException.

If a thread calls, wait() method it releases the lock immediately. It releases the lock only current object, not all locks.

wait() method throws InterruptedException.






 












Java Collection classes

Collection Framework provides an architecture to store and manipulate the group of objects.

All the operations that you perform on a data such as searching, sorting, insertion, deletion etc. can be performed by Java Collection Framework.

Collection simply means a single unit of objects. Collection framework provides many interfaces (Set, List, Queue, Deque etc.) and classes (ArrayList, Vector, LinkedList, PriorityQueue, HashSet, LinkedHashSet, TreeSet etc).


Iterator interface

Iterator interface provides the facility of iterating the elements in forward direction only.

Methods of Iterator interface

There are only three methods in the Iterator interface. They are:
  1. public boolean hasNext() it returns true if iterator has more elements.
  2. public object next() it returns the element and moves the cursor pointer to the next element.
  3. public void remove() it removes the last elements returned by the iterator. It is rarely used.
ArrayList Class:
  • uses a dynamic array for storing the elements. It extends AbstractList class and implements List interface.
  • can contain duplicate elements.
  • maintains insertion order.
  • not synchronized. not thread-safe.
  • random access because array works at the index basis.
  • manipulation slow because a lot of shifting needs to be occurred.
Example:


import java.util.*;  
class Simple{  
 public static void main(String args[]){  
  // this form of creating ArayList will accept any object to add
  // this is because, add method accepts a parameter of type Object. 
  ArrayList al=new ArrayList();  
  al.add("Ravi");  
  al.add("Vijay");  
  al.add("Ravi");  
  al.add(1.2f); // adding floating point number to ArrayList
  al.add(2);   // adding integer to ArrayList
  
  Iterator itr=al.iterator();  
  while(itr.hasNext()){  
   System.out.println(itr.next());  
  }  
 }  
}

Two ways to iterate the elements of collection:
a. Using Iterator interface.
b. Using for-each loop.

for(Object obj:al) {
    System.out.println(obj);  
}

Creating a parameterized ArrayList:

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

public class ArrayListExample {
    public static void main(String[] args) {
        ArrayList al = new ArrayList();
        al.add("raghu");
        al.add("ramya");
        al.add("raghu");
        al.add(2);
        al.add(1.2f);
        
        Iterator it = al.iterator();
        while(it.hasNext()) {
            System.out.println(it.next());
        }       
        
        // this ArrayList only accepts Strings.
        ArrayList<String> als = new ArrayList<String>();
        als.add("raghunath");
        als.add("srinath");
        // als.add(1.2f); // gives compilation error
        
        for(String name: als)
            System.out.println(name);
    }            
}

LinkedList Class:
  • uses doubly linked list to store the elements. It extends the AbstractList class and implements List and Deque interfaces.
  • can contain duplicate elements.
  • maintains insertion order.
  • not synchronized. not thread-safe.
  • No random access.
  • manipulation fast because no shifting needs to be occurred.
  • can be used as list, stack or queue.
Example:

import java.util.Iterator;
import java.util.LinkedList;

public class LinkedListExample {
    public static void main(String[] args) {
        LinkedList al = new LinkedList();
        al.add("raghu");
        al.add("ramya");
        al.add("raghu");
        al.add(2);
        al.add(1.2f);
        
        Iterator it = al.iterator();
        while(it.hasNext()) {
            System.out.println(it.next());
        }       
        
        LinkedList<String> als = new LinkedList<String>();
        als.add("raghunath");
        als.add("srinath");
        
        for(String name: als)
            System.out.println(name);
    }    
}

ListIterator Interface:

ListIterator Interface is used to traverse the element in backward and forward direction.

Methods:
  1. public boolean hasNext();
  2. public Object next();
  3. public boolean hasPrevious();
  4. public Object previous();
Example:

        LinkedList<String> als = new LinkedList<String>();
        als.add("raghunath");
        als.add("srinath");
        
        for(String name: als)
            System.out.println(name);

        Iterator it = als.iterator();
        while(it.hasNext()) {
            System.out.println(it.next());
        }
        
        System.out.println("Foward Iteration:");
        
        ListIterator fli = als.listIterator();
        while(fli.hasNext())
            System.out.println(fli.next());
        
        System.out.println("Backward Iteration:");

        ListIterator bli = als.listIterator(2);
        while(bli.hasPrevious())
            System.out.println(bli.previous());

HashSet Class:

The main difference between list and set is, set contain unique elements only. No duplicates are allowed in Set.
  • uses hashtable to store the elements.It extends AbstractSet class and implements Set interface.
  • contains unique elements only.
  • HashSet does not preserve element insertion order.  Insertion order is not maintained. To maintain insertion order, please use LinkedHashSet.
import java.util.HashSet;
import java.util.Iterator;

public class HashSetExample {
    public static void main(String[] args) {
        HashSet hs = new HashSet();
        hs.add("raghu");
        hs.add("ramya");
        hs.add("ajay");
        hs.add("vinay");
        hs.add("raghu");
        hs.add("srinath");
        hs.add("vinay");
        
        Iterator it = hs.iterator();
        while(it.hasNext())
            System.out.println(it.next());        
    }
}

o/p: You can see here, the duplicates are not printed. Also, items are in random order.
vinay
raghu
srinath
ajay
ramya

LinkedHashSet Class:
  • contains unique elements only like HashSet. It extends HashSet class and implements Set interface.
  • removes duplicates but maintains insertion order.
Example:

        LinkedHashSet al=new LinkedHashSet();  
        al.add("Ravi");  
        al.add("Vijay");  
        al.add("Ravi");  
        al.add("Ajay");  

        Iterator itr=al.iterator();  
        while(itr.hasNext()){  
         System.out.println(itr.next());  
        }

TreeSet Class:
  • contains unique elements only like HashSet. The TreeSet class implements NavigableSet interface that extends the SortedSet interface.
  • removes duplicates but maintains ascending order.
        TreeSet ts = new TreeSet();  
        ts.add("Ravi");  
        ts.add("Vijay");  
        ts.add("Ravi");  
        ts.add("Ajay");  

        Iterator titr=ts.iterator();  
        while(titr.hasNext()){  
         System.out.println(titr.next());  
        }














Java Collection Framework

Collection Framework:

An array = indexed collection + fixed number + homogeneous elements

Limitation of Arrays:
  1. Arrays are of fixed size.
  2. Arrays can hold only homogeneous data elements. However, this can be resolved using Object type arrays.
  3. Not much of ready-made methods are available with arrays. This is because, Arrays are not built based on some data structure.
To overcome these issues, Sun introduced the Collections framework.
  1. Collections are grow-able in nature dynamically.
  2. Collections can hold both homogeneous and heterogeneous objects.
  3. Lot of ready-made methods are available.
Disadvantage of Collections framework is performance.

Note:
Memory point of view, Collections are better.
Performance point of view, Arrays are better.

Meaning of Collection or Overall concept:

Group of individual objects going to be referred as a single entity is called Collection.

If we want to represent a group of individuals as a single entity then we should go for Collection.

'Collection' interface is considered as root Interface of Collection framework.

Collection is an interface.
Collections is a utility class in java.util package.

List (Interface) :
  1. Duplicates are allowed.
  2. Insertion order is preserved.
ArrayList, LinkedList, Vector and Stack are classes from List interface.

Set (Interface) :
  1. Duplicates are not allowed
  2. Insertion order is not preserved.
HashSet, LinkedHashSet are classes from Set Interface.

Set (I) --> SortedSet (I) --> NavigableSet (I) --> TreeSet (C)

If you want to represent a group of objects as key-value pairs, then we should go for Map.

Map (Interface) :
  1. Key and Values are Objects only.
  2. Key - should not be duplicate.
  3. Values - can be duplicated.
Map is not child interface of Collection.

SortedMap (Interface) :
  1. Sorting is based on Keys.
  2. SortedMap is child interface of Map.
List of Methods present in Collection Interface:
  • boolean add(Object o)
  • boolean addAll(Collection c)
  • boolean remove(Object o)
  • boolean removeAll(Collection c)
  • boolean retainAll(Collection c)
  • void clear()
  • boolean isEmpty()
  • int size()
  • boolean contains(Object o)
  • boolean containsAll(Collection c)
  • Object[] toArray()
  • Iterator iterator()
List (Interface) :

Duplicates are allowed, insertion order is preserved.
Insertion order is preserved by means of Index.
We can differentiate duplicate objects by using Index. Index place a very important role in List.
  • boolean add(int index, Object o)
  • boolean addAll(int index, Collection c)
  • Object remove(int index)
  • Object get(int index)
  • Object set(int index, Object new)
  • int indexOf(Object o)
  • int lastIndexOf(Object o)
  • ListIterator listIterator()
ArrayList :
  • Insertion order is preserved
  • Duplicate objects are allowed
  • Heterogeneous objects are allowed
  • 'null' insertion is possible, example a.add(null);
  • Best suitable for frequent retrievals
  • Is not thread-safe.
Constructors:

ArrayList al = new ArrayList()  -- this creates an ArrayList with an initial default capacity of 10.  Once ArrayList reaches its max. capacity, then a new ArrayList object will be created with  newCapacity = (currentCapacity * 3/2) + 1


Example:
import java.util.*;
public class ArrayListExample {
    public static void main(String[] args) {
        ArrayList a = new ArrayList();
        a.add("A");
        a.add(10);
        a.add("A");
        a.add(null);
        System.out.println(a); // [A, 10, A, null]
        a.remove(2);
        System.out.println(a); // [A, 10, null]
        a.add(2, "M");
        a.add("N");
        System.out.println(a); // [A, 10, M, null, N]
    }   
}


Note: In every Collection class, toString() method is overwritten to return its content directly in the following format:
[obj1, obj2, obj3 ..... obj n ]

Usually, we can use Collection to store and transfer objects. To support this requirement, every Collection class implements Serializable and Cloneable interfaces.

Frequent RETRIEVE operation - best choice is ArrayList.

Frequest INSERTION and DELETION - use LinkedList.

Vector is Thread-safe.

Since Vector is thread-safe, the performance is low compared to ArrayList.

If thread-safe is not required, use ArrayList.

How to get Synchronized version of ArrayList????

Use the Collections utility class SynchronizedList() method.

Example:
ArrayList l = new ArrayList();
List l1 = Collections.synchronizedList(l);

LinkedList :
  • Insertion order is preserved
  • Duplicates are allowed
  • Heterogeneous objects are allowed
  • null insertion is possible
  • Best suitable for frequent insertion and deletion operations
  • Not implements RandomAccess interface
 Usually, we can use LinkedLists to implement Stacks and Queues. To support this, LinkedList class defines the following specific methods:
  • void addFirst(Object o)
  • void addLast(Object o)
  • Object removeFirst()
  • Object removeLast()
  • Object getFirst()
  • Object getLast()

import java.util.*;
public class LinkedListExample {
    public static void main(String[] args) {
        LinkedList l = new LinkedList();
        l.add("durga");
        l.add(10);
        l.add(null);
        l.add("durga");
        System.out.println(l); // [durga, 10, null, durga]
        l.set(0, "software"); // set will replace at index.
        System.out.println(l); // [software, 10, null, durga]
        l.add(0, "venky"); // add will add at the index
        System.out.println(l); // [venky, software, 10, null, druga]
        l.removeLast();
        System.out.println(l); // [venky, software, 10, null]
        l.addFirst("ccc");
        System.out.println(l); // [ccc, venky, software, 10, null]
    }   
}


Vector :
  • insertion order is preserved
  • duplicates are allowed
  • heterogeneous objects are allowed
  • null insertion is allowed
  • best suitable for frequent retrievals.
  • it is thread-safe. all methods are synchronized
Vector v = new Vector(); --> creates a vector with initial capacity of 10. Once vector reaches its max capacity, a new Vector object is created with double capacity.
newCapacity = 2 * currentCapacity

Vector has few other methods which are initially added as part of Vector. (Java 1.0). Later, Vector has retrofitted with new methods of List interface to become part of Java Collection framework.

Vector.addElement() method is old one. addElement() is synchronized. add() isn't. In the Java Collections Framework, if you want these methods to be synchronized wrap the collection in Collections.synchronizedList();


Favour the use of the List methods. Like I said, if you want a synchronized List do:
List<String> list = Collections.synchronizedList(new ArrayList<String>());
list.add("hello");
import java.util.*;
public class VectorExample {
    public static void main(String[] args) {
        Vector v = new Vector();
        System.out.println(v.capacity()); // default capacity, 10
        for (int i = 1; i <= 10; i++) {
            v.addElement(i);
        }
        System.out.println(v.capacity()); // 10
        v.addElement("A");
        System.out.println(v.capacity()); // 20 - doubled the capacity
        System.out.println(v); // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, A]
    }   
}


Stack :

Child class of Vector, contains only one constructor :
Stack s = new Stack();

methods:
  • Object push(Object o)
  • Object pop()
  • Object peek() - return top of Stack
  • boolean empty()
  • int search(Object o) - returns offset from top of Stack if object available, else -1
import java.util.*;
public class StackExample {
    public static void main(String[] args) {
        Stack s = new Stack();
        s.push("A");
        s.push("B");
        s.push("C");       
        System.out.println(s.peek()); // top of stack is C
        System.out.println(s); // [A, B, C]
        System.out.println(s.search("A")); // this offset is 3
        System.out.println(s.pop()); // popped element C
        System.out.println(s); // [A, B]
        System.out.println(s.search("Z")); // -1       
    }
}

Cursors :

If we want to get objects one by one from the Collection, we should go for cursor.

3 types or cursors available in Java:
Enumeration - applicable for legacy classes, and is read-only.
Iterator - universal and all operations permitted (read and delete). Forward direction
ListIterator : bi-directional. We can perform replace and addition of new objects in addition to read and remove operations.

ListIterator is only applicable for List objects.

Iterator :

Iterator itr = c.iterator();  // c -- is any Collection class

3 methods:
  • public boolean hasNext();
  • public Object next();
  • public void remove()
HashSet -> is based on HashTable. No duplicates allowed, insertion order is not preserved.
LinkedHashSet -> No duplicates, and insertion order is preserved.
Usage: the application area of LinkedHashSet and LinkedHashMap is implementing cache applications.
SortedSet (Interface) :
To represent a group of individual objects according to some sorting order. 6 specific methods.
  • Object first() - returns the first element of sorted set.
  • Object last() - returns last element of sorted set
  • SortedSet headSet(Object obj) - returns sorted set whose elements are less than obj
  • SortedSet tailSet(Object obj) - whose elements are greater than or equal to obj
  • Comparator comparator() - returns comparator object which describes underlying sorting technique. For natural sorting order, this will return null.
public class SortedSetTest {

   public static void main(String[] args) {

      // Create the sorted set
      SortedSet set = new TreeSet(); 

      // Add elements to the set
      set.add("b");
      set.add("c");
      set.add("a");

      // Iterating over the elements in the set
      Iterator it = set.iterator();
      while (it.hasNext()) {
         // Get element
         Object element = it.next();
         System.out.println(element.toString());
      }
   }
}
Default natural sorting order for numbers is ascending order.
Default natural sorting order for characters and strings is alphabetical order (dictionary based)

TreeSet (Class) :
  • Duplicates are not allowed
  • Insertion order is not preserved
  • Heterogeneous objects are not allowed, because sorting has to be applied. In this case, ClassCastException is thrown.
  • null insertion is not possible for non-empty set. For the empty set, add first element null, insertion is possible. But after inserting null, if we try to add any element, NullPointerException is thrown.
Constructors:

TreeSet t = new TreeSet(); // default natural sorting order

TreeSet t = new TreeSet(Comparator c); // sorting order is based on Comparator object

If we are depending on natural sorting order, compulsorily objects should be homogeneous and comparable, otherwise we will get ClassCastException.

An object is said to be comparable, if the corresponding class implements Comparable Interface.

String class and all wrapper classes already implements Comparable interface where as StringBuffer class does not implement Comparable interface.

import java.util.*;
public class TreeSetExample {
    public static void main(String[] args) {
        TreeSet t = new TreeSet();
        t.add(new StringBuffer("A"));
        t.add(new StringBuffer("B"));
        t.add(new StringBuffer("Z"));
        t.add(new StringBuffer("T"));
        System.out.println(t);
    }
}

This above example will throw ClassCastException as StringBuffer does not implement Comparable interface.

Comparable Interface :

Interface present in java.lang package and contains only one method compareTo.

Any class implementing Comparable Interface has to override this method:

public int compareTo(Object obj);

Comparable meant for natural sorting order.

Comparator meant for customized sorting order.

Comparator Interface :
java.util package.
2 methods:
  • public int compare(Object obj1, Object obj2)
  • public boolean equals(Object obj)

Whenever we are implementing Comparator interface, compulsory we should provide implementation for compare() method.