Monday, November 15, 2021

Collections

 a group of individual objects as a single entity is collection

ARRAYS

COLLECTIONS

Fixed in size, i.e once we create an array we cant increase or decrease the size

Growable in nature, i.e based on our requirement we can increase or decrease the size

WRT memory arrays are not recommended to use

WRT memory cellections are always reccomended

WRT performance arrays are recommended to use

WRTperformance collections are not recomeneded to use.

Arrays can hold onlty homogenous elements

Collection can hold only heterogenous elements

There is no underlying data structure hence ready made method support is not available , hence there is a need to write method for every operation which increases complexity

Every collection class is implemented based on some standard data structure hence for every requirement readymade support is available. We need not implement those methods

Arrays can hold both primitives and objects

Collections can hold only object types not primitives.


Collection Framework: it contains several classes and interfaces which can be used to represent a group of individual objects as a single entity.

Collection is available in c++ as Container and collections framework as STL(standard framework library)

9 key interfaces of collection framework

1) Collection         2) List        3) Set        4)SortedSet        5)NavigableSet        6)Queue        7)Map    8)SortedMap        9)NavigableMap

Collection Interface: 1) if we want to represent a group of individual objects as a single entitty then we should go for collection.
2) collection interface defines the most common methods which are applicable for any collection object.
3) In general collection interface is considered as root interface of collection framework.

COLLECTION

COLLECTIONS

Collection is an interface

Collections is a class.

If we want to represent a group of individual objects as a single entity then we should go for collection

Collections is a utility class present in java.util to define several utility methods for collection objects like sort,search etc.


List(I) : List is a child interface of collection, if we want to represent a group of individual objects as a single entity where duplicates are allowed and insertion order must be preserved then we should go for list.
Note: in 1.2V vector and stack classes are re-engineered to implement list interface.

Set(I) : it is the child interface of collection, if we want to represent a group of individual objects as a single entity where duplicated are not allowed and insertion order not required then we should go for Set interface.

SortedSet(I): it is the child interface of set , if we want to represent a group of individual objects as a single entity where duplicated are not allowed and all objects should be inserted according to some sorting order then we should go for SortedSet

NavigableSet: it is the child Interface of SortedSet, it contains several methods for navigation purposes.

Differences :

List

Set

Duplicates are allowed

Duplicates are not allowed

Insertion order is preserved

Insertion order not preserved


Queue (I): Queue is the child interface of collection. if we want to represent a group of individual objects prior to processing then we should go for the queue. Queue follows FIFO order but we can implement our own priority order too.
Note: all the above interfaces are meant for representing a group of individual objects, if we want to represent a group of objects as key-value pairs then we should go for map.

Map(I) : Map is not the child interface of collection,  if we want to represent the group of objects as key-value pairs then we should go for map.

SortedMap (I): it is a child interface of Map interface. if we want to represent a group of key value pairs according to some sorting order of keys then we should go for SortedMap, in SortedMap the sorting should be based on key but not value.

NavigableMap(I): it is the child interface of SortedMap , it defines several methods for navigation purposes.

Note: the following are legacy characters present in the collection framework
1) Enumeration
2) Dictionary
3) Vector
4) Stack
5) HashTable
6) Properties
  

Collection(I) 

if we want to represent a group of individual objects as a single entity , then we should go for collection. Collection interface defines the most common methods that are applicable to any collection objects. 

boolean add(Object o)
"            addAll(Collection c)
"        "  remove(Object o)
"       "   removeAll(Collection c)
 "          retainAll(Collection c) 
void  clear()
boolean contains(Object o)
boolean containsA;;(Collection c)
boolean isEmpty()
int size();
Object[] toArray();
Iterator iterator();

there is no concrete class that implements collection interface directly


List(I) 

List is the child interface of collection.
If we want to represent a group of objects and insertion order must be preserved , then we must go for list. we can preserve insertion order via index and we can differentiate duplicate objects by using index hence index will play very important role in list.

List Interface defines the following specific methods

add(int index, E element)
Inserts the specified element at the specified position in this list (optional operation).

boolean addAll(int index, Collection c)

get(int index)
Returns the element at the specified position in this list.
remove
(int index)
Removes the element at the specified position in this list (optional operation).

indexOf(Object o)
Returns the index of the first occurrence of the specified element in this list, or -1 if this list does not contain the element.

lastIndexOf(Object o)
Returns the index of the last occurrence of the specified element in this list, or -1 if this list does not contain the element.

ListIterator listIterator();

ArrayList,LinkedList,Vector and Stack

ArrayList: the underlying data structure is a resizable array or growable array, duplicates are allowed, Insertion order is preserved, Heteregenous objects are allowed, null insertion is possible.

constructors: 
1) ArrayList l=new Arraylist();  - creates an empty arraylist object with default initial capacity 10, one arraylist reaches its max capacity a new arraylist will be created using the below-given formula.
new capacity = (current capacity *3/2) +1
2)ArrayList l=new ArrayList(int initialcapacity) - creates an empty arraylist object with specified arraylist capacity.
3) ArrayList l=new ArrayList(Collection c) - cretaes an equivalent arraylist object for given collection object.
eg:
    public static void main(String[] args) {
ArrayList lst=new ArrayList();
lst.add(4);
lst.add(8);
lst.add("A");
lst.add(2);
lst.add(null);
System.out.println(lst);
lst.remove(2);
System.out.println(lst);
lst.add(2,"M");
lst.add("N");
System.out.println(lst);
}
}
O/P: [4, 8, A, 2, null]
[4, 8, 2, null]
[4, 8, M, 2, null, N]

we can use collection to hold and transfer data from one location to other.
[Note: every collection class by default implements serealizable and cloneable interface.]

RandomAccess interface : only arraylist and vector class implement this inetrface, by implementing this we can access any random element with same speed.
it is a marker inetrface the required ability is provided by JVM , it is present in java.util package . 

Arraylist is the best choice if our frequent operation is retrieval becuase it implements randomAccess interace and it is worst choice if our frequent operation is addition or deletion 

ArrayList

Vector

Every method present in arraylist is non synchronized

Every method present in vector is synchronized

Multiple threads are allowed to operate on ArrayList

At  a time only one thread is allowed to operate and hence it is thread-safe

Relatively performance is high because threads need not wait

Relatively performance is low because threads need to wait to operate on the vector object

Introduced in 1.2V and it is non-legacy

Introduced in 1.0V and it is Legacy


how to get synchronized arraylist? 
- by default ArrayList is non synchronised but we can get synchronised version of Arraylist by using synchronizedList() method from collections class. 

ArrayList al=newArrayList();
List l1=Collections.synchronizedList(al);

similarly we get synchronised version of set and map method by using following methods of collections class
public static Set synchronizedSet(set)
public static Map synchronizedMap(map)

LinkedList

the underlying data structucture is double linked list , insertion order is preserved. duplicate objects are allowed, heterogenous objects are allowed. null insertion is possible.
LinkedList implements serealizable and cloneable interface.
LinkedList is the best choice if our frequent operation is addition/removal and worst if the frequent operation is retreival.
LinkedList l= new LinkedList(); -- creates empty linkedlist object .
LinkedList l= new LinkedList(Collection c); -- creates an equivalent linkedlist object for given collections.
ususally we can use linkedList to develop stack and queue, to support this linkedList class supports the following methods.

void addFirst(Object o)
void addLast(Object o)
Object getFirst()
Object getLast()
Object removeFirst()
Object removeLast()
eg:
public static void main(String[] args) {
LinkedList l=new LinkedList();
l.add("rohi");
l.add(12);
l.add(null);
l.set(0,"abc");
l.add(0,"def");
l.removeLast();
l.addFirst("CCC");
System.out.println(l);
}
O/P: [CCC, def, abc, 12]

ArrayList

LinkedList

Is a best choice if our frequent operation is retrieval

If the frequent operation is insertion/deletion

Worst choice for frequent insertion/deletion

Worst choice for frequent retrieval.

In array List the elements will be stored in consecutive memory location hence retrieval is easy

In LinkdedList the elements won’t be stored in consecutive memory locations



Vector

the underlying data structure is a resizable/growable array. insertion order is preserved, duplicates are allowed,heterogenous objects are allowed, null insertion is allowed, implements clonable/serializable/random access, every method present in vector is thread-safe /synchronized.

 Constructors:
1.Vector v=new Vector(); -- creates an empty vector object with default initial capacity 10 once vector reache4s its max capacity then a new vector object will b ecreated with new capacity=currentCapacity*2

2. Vector v=new Vector(int initialcapcity); - creates an empty vector object with specified capacity.

3. Vector v=new Vector(int initialCapacity, int incrementalCapacity); - 
4. Vector v=new Vector(Collection c) ; - creates a new vector with given collection 

Vector specific methods
to add objects
1) addElement(Object o)  --- vector class method
2) add(int index, Object o)
3) add(Object o) 

to remove elements
1) remove(Object o);-----C
2) remove(int index);====L
3) removeElement(Object o)-----V
4) RemoveElementAt(int index);----V
5) clear() -- collection class method
6) removeAllElements()--- Vector

to get Objects
1) Object get(int index)   ---L
2) Object elementAt(int index) ---V
3) Object firstElement()  --V
4) lastElement()---V

other methods
1) int size()
2) int capacity()
3) Enumeration elements()

eg:
public static void main(String[] args) {
Vector v=new Vector();
System.out.println(v.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
System.out.println(v); // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, A]
}
O/P: 10
10
20
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, A]

Stack

it is the child class of vector it is a specially designed class for Last in first out(LIFO) 

constructor: only one Stack s=new Stack();

Methods:
1) push()
2) pop()
3) peek() -- returns top vlaue of stack
4) boolean empty() - returns true if the stack is empty
5) int search(Object o); returns offset of the element if not found returns -1

eg:
public static void main(String[] args) {
Stack s=new Stack();
s.push("A");
s.push("B");
s.push("C");
System.out.println(s); //[A, B, C]
System.out.println(s.search("A")); //3 as offset is calculated from top
System.out.println(s.search("Z")); //-1
}
O/P:[A, B, C]
3
-1

the 3 cursors of java

 if we want to get objects one by one from the collections then we should go for cursor. there are three types of cursor available in java.
1) Enumeration
2) iteration
3) ListIterator.

Enumeration : we can use enumeration to get objects one by one from legacy collection objects. we can create enumeration objects by using elements method of Vector class.
public Enumeration elements();
eg : Enumeration e = v.elements();
methods: 
1) public boolean hasMoreElements()
2) public Object nextElement();
eg:
public static void main(String[] args) {
Vector v=new Vector();
for (int i=1;i<=10;i++){
v.addElement(i);
}
Enumeration e=v.elements();
while (e.hasMoreElements()){
Integer I= (Integer) e.nextElement();
System.out.println(I);
}
}

Limitations of Enumeration:
1) it is applicable only for legacy classes and it is not a universal cursor.
2)  by using enumeration we can get only read access and we cant perform remove operation.
to overcome the above limitations we should go for Iterator(I).

Iterator(I):

we can apply iterator concept for any collection object and hence it is the universal cursor.
by using iterator we can perform both read and remove operations.
We can create an iterator object by using iterator method of collection interface.
 public Iterator iterator() ;
eg:
 Iterator itr= collection.iterator();

Methods 
public boolean hasNext()
public Object next();
public void remove();

eg:
public static void main(String[] args) {
ArrayList list=new ArrayList();
for (int i=1;i<=10;i++){
list.add(i);
}
System.out.println(list); // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Iterator i=list.iterator();
while (i.hasNext()){
Integer I= (Integer) i.next();
if (I%2!=0) i.remove();
}
System.out.println(list); // [2, 4, 6, 8, 10]
}
O/P:[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[2, 4, 6, 8, 10]

Limitations of iterator:
1) we can only move only towards the forward direction not backward.
2) we can perform only read and remove and no other operation like add/replace.

to overcome the above limitations we should go for ListIterator.

ListIterator(I) 

It is a bidirectional cursor, i.e we can move to both forward and reverse.
By using ListIterator we can perform the addition of new objects in addition to read and remove methods.

we can create ListIterator by using listIterator method of List Interface

public ListIterator listIterator(); 

ListIterator ltr=list.listIterator();

Methods
(List Iterator is the child interface of Iterator and hence all methods available in Iterator are available to ListIterator )

ListIterator defines the following nine methods. 

forward moment:
public boolean hasNext()
public Object next()
public int nextIndex()

backword moment:
public boolean hasPrevious()
publcic Object previous()
public int previousIndex()

Extra operations:
public void remove();
public void add(Object o)
public void set(object o)

eg:
public static void main(String[] args) {
LinkedList l=new LinkedList();
l.add("hee");
l.add("huu");
l.add("heeeyy");
System.out.println(l);
ListIterator listIterator=l.listIterator();
while (listIterator.hasNext()){
String s= (String) listIterator.next();
if (s.equals("heeeyy")){
listIterator.remove();
}
else if (s.equals("huu")){
listIterator.add("huyyy");
}
else if (s.equals("hee")){
listIterator.set("hoho");
}
}
System.out.println(l);
}
O/P:
[hee, huu, heeeyy]
[hoho, huu, huyyy]

the most powerful cursor is ListIterator but its limitation is it is applicable only for list objects.

Comparison tables of three cursors

Properties

Enumeration

Iterator

ListIterator

Where we can apply

Only for Legacy classes

For Array Collection Object

Only for List Objects

Is it legacy?

Yes(1.0V)

No(1.2V)

No(1.2V)

Movement

Single directioin(only forward)

Single Direction(only forward)

Bidirectional

Allowed operations

Only Read

Read/Remove

Read/Remove/Replace/Add

How we can get?

By using elements() method in vector class

By using iterator() of Collection(I)

By using listIterator() of List(I).

Methods

2 methods

3 methods

9 methods



Vector v=new Vector();
Enumeration e=v.elements();
Iterator iterator=v.iterator();
ListIterator listIterator1=v.listIterator();
System.out.println(e.getClass().getName());
System.out.println(iterator.getClass().getName());
System.out.println(listIterator1.getClass().getName());
O/P:
java.util.Vector$1
java.util.Vector$Itr
java.util.Vector$ListItr

Set(I)



If we want to represent group of individual objects where duplicates are not allowed and insertion order is not preserved then we should go for Set(I).
Set interface doesn't contain any new method and we should use only collection interface methods.

HashSet

the underlying data structure is hashtable. duplicate objects are not allowed. insertion order is not preserved and it is based on the hashcode of objects. null insertion is possible (only once). Heterogenous objects are allowed. implements a Serializable and Cloneable but not random access interface. HashSet is the best choice if our frequent operation is search operation.

in HashSet duplicates are not allowed if we try to insert duplicates then we won't get any compile time or run time errors and add method simply returns false.

Constructors :
1) HashSet h =new HashSet(); initial capacity :16    default fill ratio :0.75
2) HashSet h=new HashSet(int initialCapacity);
3) HashSet h=new HashSet(int initialCapacity,float fillRatio);
4) HashSet h=new HashSet(Collection c);

fillRatio:  after filling a certain ratio a new HashSet object is created. 
eg:
HashSet hs=new HashSet();
hs.add("A");
hs.add(null);
System.out.println(hs.add("A"));
System.out.println(hs);
O/P:
false
[null, A]

LinkedHashSet: it is the child class of HashSet it is exactly same as HashSet including constructors and methods except for the following differences 

HashSet

LinkedHashSet

1) the underlying data structure is Hashtable

1) underlying data structure is LinkedList+HashTable

2) Insertion order is not preserved

2) Insertion order is preserved

3) introduced in version 1.2

3) introduced in version 1.4


in the above example if we replace HashSet with linkedHashSet then output insertion order will be preserved.

in general, we can use linkedHashSet to develop cache-based applications where duplicates are not allowed and insertion order preserved.

SortedSet(I) 

SortedSet is a child interface of Set, if we want to represent a group of individual objects according to some sorting order without duplicates then we should go for SortedSet.
SortedSet interface defines the following specific methods .
1) Object first() -- returns the first element of SortedSet
2) Object last() --  returns the last element of SortedSet
3) SortedSet headSet(Object obj) -- returns SortedSet whose elements are less than obj 
4) SortedSet tailSet(Object obj) -- returns SortedSet whose elements are greater than obj
5) SortedSet subSet(Object obj1,Object obj2) -- returns SortedSet whose elements are less than obj2 and greater than obj1.
6) Comparator comparator() - return comparator object that describes underlying sorting technique.

Note : for number default natural sorting order is Asceding order and for string objects in alphabetical order.

TreeSet(I)

the underlying data structure is balanced tree. duplicate objects are not allowed. insertion order is not preserved. Heterogenous objects are not allowed. Null objects are allowed only once. TreeSet implements Serializable, Cloneable but not randomaccess .all objects will be inserted based on some sorting order, it may be default natural sorting order or customized sorting order.

Constructors:
1) TreeSet t =new Treeset(); -- default natural sorting order
2) TreeSet t =new Treeset(Comparator c);  -- custom sorting order.
3)  TreeSet t =new Treeset(Collection c);
4) TreeSet t =new Treeset(SortedSet s);

public class TreeSetPract {
public static void main(String[] args) {
TreeSet t=new TreeSet();
t.add("a");
t.add("B");
t.add("c");
//t.add(2); //java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
//t.add(null); //java.lang.NullPointerException
System.out.println(t);
}
Output: [B, a, c]

null acceptance:  1) if we insert null in non empty TreeSet , then we will get NPE as it will compare existing elements and null 
2) if we insert null in empty TreeSet, we will be able to add null as first element but after insereting that null if we are trying to insert any other we will get runtime exception saying NPE. ( from 1.7V onwards null is not at all allowed in Treeset)

eg: if we are depending on default natural sorting order , the objects should be homogenous and comparable otherwise we will runtime exception (i.e class cast exception) . 
the object is comparable iff corresponding classes implements comparable interface. String class and all wrapper classes implement comparable interface whereas StringBuffer do not implement comparable interface like shown below.
public static void main(String[] args) {
TreeSet t=new TreeSet();
t.add(new StringBuffer("a"));
t.add(new StringBuffer("B"));
System.out.println(t);
}
O/P: java.lang.ClassCastException: java.lang.StringBuffer cannot be cast to java.lang.Comparable

Comparable(I): it is present in java.lang package and it contains only 1 method compareTo() .
public int compareTo(Object obj) 
obj1.compareTo(obj2) - returns -ve if obj1 has to come before obj2
return +ve if obj1 has to come after obj2
returns 0 if obj1 and obj2 are equal
eg:
System.out.println("A".compareTo("Z"));
System.out.println("Z".compareTo("K"));
System.out.println("A".compareTo("A"));
O/P:
-25
15
0

if we are dependent on default natural sorting order while adding objects to Treeset , JVM will call compareTo() method .
TreeSet t=new TreeSet();
t.add("A");
t.add("K");  "K".compareTo("A") (-ve)
t.add("Z");   "Z".compareTo("K") (+ve)
t.add("Z");   "Z".compareTo("Z") ( 0 )
[A,K,Z]

if default natural sorting order is not available or if you are not satisfied with default natural sorting then we can go for customized sorting using Comparator.

Comparable is meant for default natural sorting order whereas Comparator is meant for customized sorting order.

Comparator

Comparator interface present in java.util packageand it defines two methods compare() and equals().
1) public int compare(Object obj1, Object obj2); -->  returns -ve if obj1 has to come before obj2
return +ve iff obj1 has to come after obj2
returns 0 iff obj1 and obj2 are equal
2) public boolean equals(Object obj) 

Whenever we are implementing comparator interface compulsorily we should provide implementation only for compare method and we are not required to provide implementation for equals method, because it is already available in Object class.

write a program to insert integer objects into the treeset where the sorting order is descending order.
public class CompartorPract implements Comparator {
@Override
public int compare(Object o1, Object o2) {
Integer i1=(Integer)o1;
Integer i2=(Integer)o2;
if (i1<i2) return +1;
else if (i1>i2)return -1;
else return 0;
}
}
class Comp{
public static void main(String[] args) {
TreeSet t=new TreeSet(new CompartorPract());
t.add(10);
t.add(0); // compare(0,10);
t.add(5);
t.add(20);
System.out.println(t);
}
}
O/P: [20, 10, 5, 0]
 
at line 1 if we are not passing comparator object then internally JVM will call compareTo() method which is meant for default natural sorting order. if we pass the comparator object then the JVM will call compare method which is meant for customized sorting.

various possible implementations of the compare method

1)  the return statements given in the compare method signify different cases.
public class CompartorPract implements Comparator {
@Override
public int compare(Object o1,Object o2)
{
Integer i1=(Integer)o1;
Integer i2=(Integer)o2;
return i1.compareTo(i2); //[0,5,10,20]
return -i1.compareTo(i2); //[20, 10, 5, 0]
return i2.compareTo(i1); // descending order[20, 10, 5, 0]
return -i2.compareTo(i1); // ascending order[0,5,10,20]
return +1; // insertion order [10,0,5,20]
return -1; //reverse [20,5,0,10]
return 0; //only first element will be inserted [10]
}
}
class Comp{
public static void main(String[] args) {
TreeSet t=new TreeSet(new CompartorPract());
t.add(10);
t.add(0); // compare(0,10); +ve to the right
t.add(5); // compare(5,10); +ve compare(5,0) -ve left
t.add(20);// compare(20,10) -ve to the left
System.out.println(t);
}
}

Write a program to insert string objects in TreeSet where all elements should be inserted according to reverse of alphabetical order.
public class ComparatorPract2 implements Comparator {
@Override
public int compare(Object o1, Object o2) {
String s1=o1.toString();
String s2=(String)o2;
return -s1.compareTo(s2);
}
}
class A {
public static void main(String[] args) {
TreeSet t=new TreeSet(new ComparatorPract2());
t.add("romy");
t.add("somy");
t.add("bhumi");
t.add("namami");
System.out.println(t);
}
}
O/P: [somy, romy, namami, bhumi]

Write a program to insert StringBuffer objects into TreeSet where sorting order is alphabetical.
public class ComparatorPract2 implements Comparator {
@Override
public int compare(Object o1, Object o2) {
String s1=o1.toString();
String s2=o2.toString();
return s1.compareTo(s2);
}
}
class A {
public static void main(String[] args) {
TreeSet t=new TreeSet(new ComparatorPract2());
t.add(new StringBuffer("romy"));
t.add(new StringBuffer("somy"));
t.add(new StringBuffer("bhumi"));
t.add(new StringBuffer("namami"));
System.out.println(t);
}
}
O/P: [bhumi, namami, romy, somy]

Note : if we are depending on default natural sorting order compulsorily the objects should be homogenous and comparable. other wise we will get class cast exception. 
if we are defining our own sorting by comparator then objects need not be comparable and homogenous i.e we can heterogenous non-comparable objects also.

Write a  program to insert String and StringBuffer objects into TreeSet where sorting order is increasing length order. if two objects have the same length then consider their alphabetical order.
public class ComparatorPract2 implements Comparator {
@Override
public int compare(Object o1, Object o2) {
String s1=o1.toString();
String s2=o2.toString();
int l1=s1.length(),l2=s2.length();
if (l1<l2) return -1;
else if (l1>l2) return +1;
else s1.compareTo(s2);
return s1.compareTo(s2);
}
}
class A {
public static void main(String[] args) {
TreeSet t=new TreeSet(new ComparatorPract2());
t.add(new StringBuffer("rom"));
t.add(new StringBuffer("som"));
t.add(new StringBuffer("somy"));
t.add(new StringBuffer("bhumi"));
t.add(new StringBuffer("namami"));
System.out.println(t);
}
}
O/P: [rom, som, somy, bhumi, namami]
as we use our own compare method we can add heterogeneous objects.

1)for predefined comparable classes default natural sorting order is already available, if we are not satisfied with that sorting order then we can define our own sorting using comparator.
2) for predefined non -comparable CLASSES LIKE sTRINGbUFFER DEFAULT NATURAL SORting order not already available we can define our own sorting by using comparator.
3) for our own classes like employee , the person who is writing the class is responsible to define sorting order by implementing comparable interface.
4) the one who uses the class if is not satisfied with default natural sorting order then he can define own sorting by comparator.

write a program to implement Comparable and Comparator
public class ComparablePract3 {
public static void main(String[] args) {
Emp e1=new Emp(1,"zsd");
Emp e2=new Emp(2,"esd");
Emp e3=new Emp(3,"asd");
Emp e4=new Emp(4,"ysd");
TreeSet<Emp> t=new TreeSet<>(new MyComp());
t.add(e1);
t.add(e2);
t.add(e3);
t.add(e4);
System.out.println(t);
TreeSet<Emp> t1=new TreeSet<>();
t1.add(e1);
t1.add(e2);
t1.add(e3);
t1.add(e4);
System.out.println(t1);
}
}
class Emp implements Comparable{
int id;
String name;
Emp(int i,String s){
this.id=i;
this.name=s;
}
@Override
public String toString() {
return "Emp{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
@Override
public int compareTo(Object o) {
Emp e1=this;
Emp e2=(Emp)o;
if (e1.id>e2.id) return +1;
else if (e1.id<e2.id) return -1;
return 0;
}
}
class MyComp implements Comparator{

@Override
public int compare(Object o1, Object o2) {
Emp e1=(Emp) o1;
Emp e2=(Emp) o2;
String s1=e1.name,s2=e2.name;
return s1.compareTo(s2);
}
}
O/P:
[Emp{id=3, name='asd'}, Emp{id=2, name='esd'}, Emp{id=4, name='ysd'}, Emp{id=1, name='zsd'}]
[Emp{id=1, name='zsd'}, Emp{id=2, name='esd'}, Emp{id=3, name='asd'}, Emp{id=4, name='ysd'}]

Comparable

Comparator

1)meant for Default natural sorting order

1) meant for customized sorting method

2) defined in java.lang package

2) defined in java.util package

3) only one method compareTo()

3) two methods compare() and equals()

4) String class and all wrapper classes implement Comparable

4) the only implementing class is Collator and RuleBasedCollator


comparison of Set implemented classes.

Property

HashSet

LinkedHashSet

TreeSet

Underlying data set

Hash table

Linked list+ hash table

Balanced tree

Duplicate objects

Not allowed

Not allowed

Not allowed

Insertion order

Not preserved

Preserved

Not preserved

Sorting Order

N/A

N/A

Applicable

Heterogenous object

Allowed

Allowed

Not allowed

Null acceptance

Allowed

Allowed

Not allowed from version 1.6 onwards


Map(I)

Map is not a child interface of collection . if we want to represent a group of objects as key,valuepairs then we should go for Map.
both keys and values are objects only.
duplicate keys are not allowed but values can be duplicated.
each key,value pair is called entry hence map is considered as a collection of entry objects.

methods in Map interface.
1) Object put(Object key, Object pair);  -- to add one key-value pair to the map and if the key is already present then the old value will be replaced with new value and it return new value.
2) void putAll(Map m) 
3) Object get(Object key)  -- returns the value associated with specific key.
4) Object remove(Object key) 
5) boolean containsKey(Object key)
6) boolean containsValue(Object value)
7) boolean isEmpty()
8) int size()
9) void clear();
collection views of map methods.
10) Set keySet(); -- set of keys
11) Collection values() ; -- set of values
12) Set entrySet(); -- set of entries.

Entry(I): a map is a group of key, value pairs and each key,value pairs is called an Entry . map is considered as a group of entry Objects. without map object there is no chance of existing entry without Map . Entry interface is define inside Map interface .
interface Map
{
interface Entry
{
Object getKey();
Object getValue(); -- these are Entry specific methods and apply only on Entry objects.
Object setValue();
}
}

HashMap(I):

1) underlying data structure is Hashtable
2) insertion order is not preserved and it is based on hashcode of keys
3) duplicate keys are not allowed.
4) duplicate values are allowed.
5) heterogenous keys, values are allowed.
6) Null key is allowed only once and null value is allowed any times.
7) implements Serializable and Cloneable interfaces but not RandomAccess.
8) is the best choice if our frequent operation is search operation.

Constructors:
1) HashMap hm=new HashMap(); -- default capacity is 16 and default fill ratio is 0.75
2) HashMap hm=new HashMap(int initialCapacity); -- creates an empty hashmap object with specified initial capacity.
3) HashMap hm=new HashMap(int initialCapacity , float fillRatio);
4) HashMap hm=new HashMap(Map m);

EG:
public class MapPract {
public static void main(String[] args) {
HashMap mp=new HashMap();
mp.put("romy",100);
mp.put("somy",103);
mp.put("tomy",106);
mp.put("bomy",112);
System.out.println(mp);
System.out.println(mp.put("romy",200));
Set s=mp.keySet();
System.out.println(s);
Collection c=mp.values();
System.out.println(c);
Set s1=mp.entrySet();
System.out.println(s1);
Iterator iterator=s1.iterator();
while (iterator.hasNext())
{
Map.Entry entry=(Map.Entry)iterator.next();
if (entry.getKey().equals("tomy")){
entry.setValue(107);
}
}
System.out.println(mp);
}
}
O/P: {tomy=106, bomy=112, romy=100, somy=103}
100
[tomy, bomy, romy, somy]
[106, 112, 200, 103]
[tomy=106, bomy=112, romy=200, somy=103]
{tomy=107, bomy=112, romy=200, somy=103}

Differences between HashMap and HashTable.

HashMap

HashTable

Not synchronized

Synchronized

Multiple threads allowed, hence not thread safe

Only one thread is allowed at time on hashtable, hence thread-safe

Compraritively speed as there is no wait time for threads

Comparatively slow as there is wait time for threads

Null key or value is allowed

Null key or value is  not at all allowed

Not a legacy introduced in 1.2V

It is a legacy introduced in 1.0V


how to get a synchronized version of HashMap.
- by default HashMap is non Synchronized but we can get synchronized version of HAshMAp by using synchronizedMap() method of collections class.
HashMap mp=new HashMap();
Map mp1=Collections.synchronizedMap(mp);

LinkedHashMap

it is the child class of HAshMap and exactly same as HashMap except the underlying data structure is HashTable + LinkedList and the insertion order is preserved.

HashMap

LinkedHashMap

The underlying data structure is HashTable

The underlying data structure is LinkedList+ HashTable(hybrid)

Insertion order is not preserved

Insertion order is preserved

Introduced in version 1.2V

Introduced in version 1.4V


in the previous example if we replace HashMap with LinkedHashMap then the insertion order will be preserved.
[Note: LinkedHashSet and LinkedHashMap are used for cache-based applications.] 

== operator and .equals()  : == meant for address/reference comparison and equals() method is meant for content comparison.
JVM calls equals() method to verify uniqueness of key in HashMap.

IdentityHashMap:

it is exactly same as HashMap including methods and constructors except the following change.
in the normal HashMap, JVM will use equals() method which is meant for content comparison, whereas in IdentityHashMAp JVM will use == method for content comparison.

eg:
   public static void main(String[] args) {
HashMap m1=new HashMap();
IdentityHashMap m2=new IdentityHashMap();
Integer i1=new Integer(10);
Integer i2=new Integer(10);
m1.put(i1,"abc");
m1.put(i2,"def");
m2.put(i1,"abc");
m2.put(i2,"def");
System.out.println(m1);
System.out.println(m2);
}
}
O/P:{10=def}
{10=def, 10=abc}

on replacing HashMap with IdentityHashMap the i1 and i2 are not duplicate keys because i1==i2 is false.

WeakHashMap : It is exactly the same as HAshMap except that in the case of HashMap even though object doesnt have any reference it is not eligible for any  GC if it is associated with HashMAp , i.e HAshMAp dominates GC . in WEakHAshMap if Object doesn't contain any references it is eligible for GC even though object associated with WeakHAshMap. i.eGarbage collector dominates WeakHashMap
public class WeakHashMapPract  {
public static void main(String[] args) throws InterruptedException {
HashMap m=new HashMap();
WeakHashMap m1=new WeakHashMap();
Temp t=new Temp();
Temp t1=new Temp();
m.put(t,"abc");
m1.put(t1,"abc");
t=t1=null;
System.gc();
Thread.sleep(1000);
System.out.println(m);
System.out.println(m1);
}
}
class Temp{
@Override
public String toString() {
return "temp";
}
@Override
public void finalize()
{
System.out.println("finalize method called");
}
}
eg: finalize method called
{temp=abc}
{}

the object t1 was collected by GC whereas t couldnt be collected.

SortedMap(I)

It is the child interface of Map, if we wanty to represent a group objects as key-value pairs accoording to some sorting order of keys then we should go for SortedMap(I).
Sorting is based on the key but not based on value.

methods available in SortedMap:
Object firstKey();
Object lastKey();
SortedMap headMAp(Object key);
SortedMap TailMap(Object key);
SortedMap subMap(Object key);
Comparator comparator();   - returns null for Default natural sorting.

TreeMap()

the underlying dta structure is red-black tree.
insertion order is not preserved and it is based on some sorting order of keys.
duplicate keys are not allowed but values can be duplicated.
if we aere depenmding on default natural sorting order then kesys should be homogenous and comparable otherwise we will get runtime exception saying classCastException, if we are defining our own sorting by comparator then keys need not be homogenous and comparable. we can take heterogenous non-comparable objects also . 
wheter we are depending on defaulty natural sorting order or customised sorting orde rthere are not restrictions for values. we can take heterogenous non-comparable objects also.

null acceptance: null key are not at all allowed from version 1.7 Version onwards . prior to that null could be entered to only empty treemap as a first entry only. for values we can add null any number of times.

constructors:

TreeMap t=new TreeMap();
TreeMap t1=new TreeMap(Comparator c);
TreeMap t=new TreeMap(SortedMap m);
TreeMap t=new TreeMap(Map m);

Example for Default natural sorting order:
public static void main(String[] args) {
TreeMap m=new TreeMap();
m.put(100,"abc");
m.put(101,"aaa");
m.put(103,"def");
m.put(111,112); //accepted
//m.put("key","value"); -java.lang.ClassCastException: Integer cannot be cast to String
//m.put(null,"null"); -NullPointerException
System.out.println(m);
}
O/P: {100=abc, 101=aaa, 103=def, 111=112}

Example for Customised sorting:
public class TreeMapPract {
public static void main(String[] args) {
TreeMap m=new TreeMap(new Mycomp());
m.put("hij",40);
m.put("abc",10);
m.put("aaa",20);
m.put("def",30);
System.out.println(m);
}
}
class Mycomp implements Comparator{
@Override
public int compare(Object o1, Object o2) {
String s1=o1.toString(),s2=o2.toString();
return s2.compareTo(s1);
}
}
O/P: {hij=40, def=30, abc=10, aaa=20} 

HashTable

the underlying data structure for HashTable is Hashtable . 
Insertion order is not preserved and it is based on hashCode of keys.
duplicate keys are not allowed and values can be duplicated.
heterogenous objects are allowed for both keys and values.
null is not allowed for both key and value otherwise we will get NPE.
it implements serializable and cloneable interfaces but not RandomAccess.
every method present in hashtable is synchronized and hence HAshTable object is Thread safe
HashTable is  the best choice if our frequent operatioin is search operation.

Constructors.

1) Hashtable h=new Hashtable(): - creates new hashtable with default initial capacity 11 and default fill ratio 0.75
2) Hashtable h=new Hashtable(int initialCapacity);
3) Hashtable h=new Hashtable(int initialCapacity, float fillRatio);
4) Hashtable h=new Hashtable(Map m)

public class HashTablePract {
public static void main(String[] args) {
Hashtable mp=new Hashtable();
mp.put(new Tmp(5),100); // will be placed in 5th position
mp.put(new Tmp(2),103); // will be placed in 2nd position
mp.put(new Tmp(6),106); // will be placed in 6th position
mp.put(new Tmp(15),112);// will be placed in 15%11=4th position
mp.put(new Tmp(23),113);//will be placed in 23%11=1st position
mp.put(new Tmp(16),114);//will be placed in 15%11=5th position
System.out.println(mp); // output is top-to-bottom
}
}
class Tmp
{
int i;

public Tmp(int i) {
this.i = i;
}
public int hashCode()
{
return i;
}
@Override
public String toString() {
return i+"";
}
}
O/P:{6=106, 16=114, 5=100, 15=112, 2=103, 23=113}
in the above example if we change the hashCode method as 
 public int hashCode()
    {
        return i%9;
    }
then the output will be {16=114, 15=112, 6=106, 23=113, 5=100, 2=103}

3) if we configure initial capacity as 25 i.e Hashtable mp=new Hashtable(25); 
then the output will be  {23=113, 16=114, 15=112, 6=106, 5=100, 2=103}

Properties

in our program, if anything which changes frequently(like username/password/mailid) are not recommended to hardcode in java program . because if there is any change to reflect that change recompilation.rebuild and redeploy application are required even at times server restart is also needed which creates a big business impact to the client.
we can overcome this problem by using properties file , such type of the variable things we have to configure in the properties file. fromthe properties file we have to read into java program and we can use those properties, the main advantage of this approach is if there is a change in properties file to reflect that change just redeployment is enough which won't create any business impact to the client.
we can use java properties object to hold properties which are coming from properties file.
inh normal map (HashMap,HashTable,TreeMonap) any type of values can be key and value but in the case of properties key and value should be string.

Constructors

1) Properties p=new Properties();

Methods:
1) String getProperty(String pname) - to set a new property
2) String setProperty(String pname,String pvalue) - to get value associated with specific property.
3) Enumeration propertyNames() 
4) void load(InputStream is) - to load properties from properties file into java properties object
5) void store(OutputStream os,String comment) - to store properties from java properties object into properties file.

example: 
abc.properties 
ruby=222
kukkuti=333
romy=111
public class PropertiesPract {
public static void main(String[] args) throws IOException {
Properties p=new Properties();
FileInputStream fis=new FileInputStream("C:\\Users\\rohithrh\\IdeaProjects\\Practice\\src\\kalekshan\\abc.properties");
p.load(fis);
System.out.println(p);
System.out.println(p.getProperty("kukkuti"));
p.setProperty("haha","8090");
FileOutputStream fos=new FileOutputStream("C:\\Users\\rohithrh\\IdeaProjects\\Practice\\src\\kalekshan\\abc.properties");
p.store(fos,"updated by rohii");
}
}
O/P: {ruby=222, kukkuti=333, romy=111}
333

abc.properties - after the code execution
#updated by rohii
#Wed Oct 27 16:39:16 IST 2021
ruby=222
kukkuti=333
romy=111
haha=8090

1.5 Version enhancements (Queue interface)

it is the child interface of Collection, if we want to represent a group of individual object prior to precessing then we should go for queue. eg: before sending sms broadcast we have to store mobile number the message should be delivered in the same order for this requirement Queue is the best choice.
usually queue follows FIFO order but based on our requirement we can implement our own priority order also (PriorityQueue)
fromV1.5 onwards LinkedList class also implements Queue .
LinkedList based implementation of Queue always follows FIFO order.

Queue interface-specific methods
1) offer(Object o) to add an object into the queue
2) poll() - remove head element of the queue. returns null if queue is empty
3) remove() - to retrun the head element of the queue. if queue is empty then NoSuch ElementException
4) peek() - to remove return head element of the queue. if empty the 'null'
5) element() - to remove and return head element of the queue.if empty then 'NoSuchElementException'

PriorityQueue

1) if we wqant to represnt a group of individual objects prior to processing accroding to some priority then we should go for Priority queue.
2) the priority can be either Default natrakl sorting order or customised sorting order defined by comparator.
3) insertoion order is not preserved and it is baded on some priority .
4) duplicate objects are not allowed.
5) if we are depending on default natural sorting order compulsorily objects should homogenous and comparable otherwise we will get ClassCast Exception .
6) if we are defining our own sorting by comparator then objects need not be homogenous and comparable.
7) null is not allowed even as a first element also.

Constructors

1) PriorityQueue pq=new PriorityQueue(); -- creates an empty priority queue with default initial capacity 11 and all objects are inserted according to Default natural sorting order
2) PriorityQueue pq=new PriorityQueue(int initialCapacity); -- creates an empty priority queue with given initial capacity 
3) PriorityQueue pq=new PriorityQueue(int initialCapacity,Comparator c);
4) PriorityQueue pq=new PriorityQueue(SortedSet s);
5) PriorityQueue pq=new PriorityQueue(Collection c);

public static void main(String[] args) {
PriorityQueue pq=new PriorityQueue();
System.out.println(pq.peek()); //null
//System.out.println(pq.element()); //NoSuchElementException
for (int i=0;i<=10;i++){
pq.offer(i);
}
System.out.println(pq);
System.out.println(pq.poll());
System.out.println(pq);
}
O/P:
null
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
0
[1, 3, 2, 7, 4, 5, 6, 10, 8, 9]

Note: Some platforms won't provide support for thread priorities and Priority queues 

1.6Version enhancements in Collection framework

in 1.6version the following two concepts were included in the collection framework.

NavigableSet

it is the child interface of SortedSet(I) and it defines several methods for navigation purposes. 

NavigableSet defines the following methods./
ceiling(E e)
Returns the least element in this set greater than or equal to the given element, or null if there is no such element.
Iterator<E> descendingIterator()
Returns an iterator over the elements in this set, in descending order.
NavigableSet<E> descendingSet()
Returns a reverse order view of the elements contained in this set.
E floor(E e)
Returns the greatest element in this set less than or equal to the given element, or null if there is no such element.
SortedSet<E> ceiling(E e)
Returns the least element in this set greater than or equal to the given element, or null if there is no such element.
Iterator<E> descendingIterator()
Returns an iterator over the elements in this set, in descending order.
NavigableSet<E> descendingSet()
Returns a reverse order view of the elements contained in this set.
E floor(E e)
Returns the greatest element in this set less than or equal to the given element, or null if there is no such element.
SortedSet<E> headSet(E toElement)
Returns a view of the portion of this set whose elements are strictly less than toElement.
NavigableSet<E> headSet(E toElement, boolean inclusive)
Returns a view of the portion of this set whose elements are less than (or equal to, if inclusive is true) toElement.
higher(E e)
Returns the least element in this set strictly greater than the given element, or null if there is no such element.
Iterator<E> iterator()
Returns an iterator over the elements in this set, in ascending order.
lower(E e)
Returns the greatest element in this set strictly less than the given element, or null if there is no such element.
pollFirst()
Retrieves and removes the first (lowest) element, or returns null if this set is empty.
pollLast()
Retrieves and removes the last (highest) element, or returns null if this set is empty.
NavigableSet<E> subSet(E fromElement, boolean fromInclusive, E toElement, boolean toInclusive)
Returns a view of the portion of this set whose elements range from fromElement to toElement.
SortedSet<E> subSet(E fromElement, E toElement)
Returns a view of the portion of this set whose elements range from fromElement, inclusive, to toElement, exclusive.
SortedSet<E> tailSet(E fromElement)
Returns a view of the portion of this set whose elements are greater than or equal to fromElement.
NavigableSet<E> tailSet(E fromElement, boolean inclusive)
Returns a view of the portion of this set whose elements are greater than (or equal to, if inclusive is true) fromElement.(E toElement)
Returns a view of the portion of this set whose elements are strictly less than toElement.
NavigableSet<E> headSet(E toElement, boolean inclusive)
Returns a view of the portion of this set whose elements are less than (or equal to, if inclusive is true) toElement.
higher(E e)
Returns the last element in this set strictly greater than the given element, or null if there is no such element.
Iterator<E> iterator()
Returns an iterator over the elements in this set, in ascending order.
lower(E e)
Returns the greatest element in this set strictly less than the given element, or null if there is no such element.
pollFirst()
Retrieves and removes the first (lowest) element, or returns null if this set is empty.
pollLast()
Retrieves and removes the last (highest) element, or returns null if this set is empty.
NavigableSet<E> subSet(E fromElement, boolean fromInclusive, E toElement, boolean toInclusive)
Returns a view of the portion of this set whose elements range from fromElement to toElement.
SortedSet<E> subSet(E fromElement, E toElement)
Returns a view of the portion of this set whose elements range from fromElement, inclusive, to toElement, exclusive.
SortedSet<E> tailSet(E fromElement)
Returns a view of the portion of this set whose elements are greater than or equal to fromElement.
NavigableSet<E> tailSet(E fromElement, boolean inclusive)
Returns a view of the portion of this set whose elements are greater than (or equal to, if inclusive is true) fromElement.

    public static void main(String[] args) {
TreeSet<Integer> treeSet=new TreeSet<Integer>();
treeSet.add(1000);
treeSet.add(2000);
treeSet.add(3000);
treeSet.add(4000);
treeSet.add(5000);
System.out.println(treeSet);
System.out.println(treeSet.ceiling(2000));
System.out.println(treeSet.higher(3000));
System.out.println(treeSet.floor(3000));
System.out.println(treeSet.lower(3000));
System.out.println(treeSet.pollFirst());
System.out.println(treeSet.pollLast());
System.out.println(treeSet.descendingSet());
System.out.println(treeSet);
}
}
O/P:
[1000, 2000, 3000, 4000, 5000]
2000
4000
3000
2000
1000
5000
[4000, 3000, 2000]
[2000, 3000, 4000]

NavigableMap(I)

it is the child interface of SortedMap(I) it defines several methods for navigatioin purposes.

NavigableMap defines the following methods.

1)floorKey()
2)lowerKey()
3)higherKey()
4)ceilingKey()
5) higherKey()
6) pollFirstEntry()
7) pollLastEntry()
8) descendingMap()

example
public static void main(String[] args) {
TreeMap<String,String> t=new TreeMap<String,String>();
t.put("b","banana");
t.put("c","cat");
t.put("d","dog");
t.put("g","goat");
System.out.println(t);
System.out.println(t.ceilingKey("c"));
System.out.println(t.higherKey("e"));
System.out.println(t.floorKey("e"));
System.out.println(t.lowerKey("e"));
System.out.println(t.pollFirstEntry());
System.out.println(t.pollLastEntry());
System.out.println(t.descendingMap());
System.out.println(t);
}
O/P:
{b=banana, c=cat, d=dog, g=goat}
c
g
d
d
b=banana
g=goat
{d=dog, c=cat}
{c=cat, d=dog}

Collections

it is a utility class to provide several utility methods for collection objects like sorting, searching, reversing, etc. 

collections class defines the following two sort methods.

1) public static void sort(List l) -- 
in this case list should compulsorily contain homogenous and comparable objects otherwise we will get runtime exception saying classCastException.
list should not contain null, else we will get NPE.

public static void main(String[] args) {
ArrayList lst = new ArrayList();
lst.add(4);
lst.add(8);
// lst.add("A"); --classCastException
lst.add(2);
// lst.add(null); -- NPE
Collections.sort(lst);
}
O/P: [2, 4, 8]

2) public static void sort(List l, Comparator c) - for customised sorting 

eg:
public class CollectionsSortPract {
public static void main(String[] args) {
ArrayList lst=new ArrayList();
lst.add("S");
lst.add("W");
lst.add("A");
lst.add("Z");
System.out.println(lst);
Collections.sort(lst,new MyComp3());
System.out.println(lst);
}
}
class MyComp3 implements Comparator{

@Override
public int compare(Object o1, Object o2) {
String s1=o1.toString();
String s2=o2.toString();
return s2.compareTo(s1);
}
}
O/P: 
[S, W, A, Z]
[Z, W, S, A]

searching for an element in the list.

Collections class define the following binary search methods
1) public static int binarySearch(List l,Object target) - if the list is sorted according to default natural sorting order then we should go for this method.

2) public static int binarySearch(List l, Object target,Comparator c)  - if the list is sorted according to customised natural sorting order then we should go for this method.

Conclusions:
1) the above search methods will use binary search algorithm internally , succesful search returns index and unsuccessful search returns insertion point. the insertion point is the location where we can place target element in the list.
2) before calling binary search method  compulsorily list should be sorted. otherwise we will get unpredicted results.
3) if the list is sorted according to comparator then at the time of search operation we have to pass the same comparator object.

eg:
public static void main(String[] args) {
ArrayList al=new ArrayList();
al.add("Z");
al.add("A");
al.add("M");
al.add("K");
al.add("a");
System.out.println(al);
Collections.sort(al);
System.out.println(al);
System.out.println(Collections.binarySearch(al,"Z"));
System.out.println(Collections.binarySearch(al,"J"));//insertion point is output
}
O/P:
 [Z, A, M, K, a]
[A, K, M, Z, a]
3
-2

for the list of  'n' elements in binary search
1) successful search result range 0 to n-1
2) unsuccessful search result range  -(n+1) to -1
3) total result range -(n+1) to n-1

collections class defines the following reverse method to reverse the list
public static void reverse(List l)

public static void main(String[] args) {
ArrayList lst=new ArrayList();
lst.add(4);
lst.add(8);
lst.add(45);
lst.add(2);
lst.add(5);
System.out.println(lst);
Collections.reverse(lst);
System.out.println(lst);
}
O/P:
[4, 8, 45, 2, 5]
[5, 2, 45, 8, 4]

reverse() versus reverseOrder()

we can use the reverse() method to reverse elements of the list, whereas reverseOrder() is used to get reverse comparator

Comparator c1=Collections.reverseOrder(Comparator c)

Arrays

arrays class is a utility class to define several utility methods for arrays or array objects.

1) Sorting elements of an array: arrays class define the following sort methods to sort elements of primitive and object type arrays.

public static void sort(primirtive[] p) - sort primititive

public static void sort(Object[] o) - sort objects

public static void sort(Object[] o, Comparator c)

public static void main(String[] args) {
int[] a={6,7,1,3,2};
for (int i:a){
System.out.print(i+" ");
}
System.out.println("");
Arrays.sort(a);
for (int i:a){
System.out.print(i+" ");
}
System.out.println("");
String[] s={"w","b","a"};
Arrays.sort(s);
for (String s1:s)
System.out.print(s1+" ");
}
O/P: 
6 7 1 3 2 
1 2 3 6 7 
a b w

Note: we can sort primitive arrays only based on default natural sorting order whereas we can sort Objects based on default natural sorting order or based on customized sorting order.

Searching in array

1) public static int binarySearch(primitive[] p,primitive target)
2) public static int binarySearch(Object[] o,Object target)
3) public static int binarySearch(Object[] o,Object target,Comparator c)

Arrays class binary search method is exactly the same as Collections class binary search method.

Conversion of Array to List

public static List asList(Object[] o)
- this method won't create an independent list object, for the existing array we are getting list view.
String[] s={"A","e","T"};
List l=Arrays.asList(s);

by using array reference if we perform any change automatically that change will be reflected in the List similarly by using list reference if we perform any change that change will be reflected automatically to the array.
by using list reference we can't perform any operation which varies the size else we will get runtime exception of UnsupportedOpeartionException

by using list refere3nce we are not allowed to replace with heterogenous objects otherwise we will get runtime exception i.e ArrayStoreException

l.set(1,new Integer(19)); --> exception

public static void main(String[] args) {
String[] s={"A","e","T"};
List l= asList(s);
System.out.println(l);
l.set(1,"L");
for (String s1:s){
System.out.println(s1);
}
l.add("abc"); //UnsupportedOperationException
l.remove(2);//UnsupportedOperationException
l.set(1,new Integer(11)); //ArrayStoreException
}
Output: 
[A, e, T]
A
L
T

Generics

the main objective is to provide type safety and to resolve type casting problems.

case1 Type-safety: arrays are type-safe, i.e we can give the guarantee for the type of elements present inside the array eg: if our programming requirement is to hold only string type objects we can choose String array, hence if we try to add any other type of objects we will get compile-time errors. String array can contain only String type of objects.
Collections are not type-safe , we cant guarantee same type of elements in collection.

case 2 type-casting : in the case of arrays at the time of retreival it is not required to perform type-casting because the type is gauranteed. but in the case of collections at the time of retreival we have to perform type casting mandatorily.
eg:
ArrayList l=new ArrayList();
l.add("rohi");
String name=(String) l.get(0);

to overcome above problems of collections some people introduced generics concepts in 1.5Version , the main objective is to provide type safety and to resolve type-casting problems.

eg:
ArrayList<String> al=new ArrayList<String>();
al.add("aaa");
al.add(new Integer(10));
O/P:
Error:(12, 11) java: no suitable method found for add(java.lang.Integer)
    method java.util.Collection.add(java.lang.String) is not applicable
      (argument mismatch; java.lang.Integer cannot be converted to java.lang.String)
    method java.util.List.add(java.lang.String) is not applicable
      (argument mismatch; java.lang.Integer cannot be converted to java.lang.String)
    method java.util.AbstractCollection.add(java.lang.String) is not applicable
      (argument mismatch; java.lang.Integer cannot be converted to java.lang.String)
    method java.util.AbstractList.add(java.lang.String) is not applicable
      (argument mismatch; java.lang.Integer cannot be converted to java.lang.String)
    method java.util.ArrayList.add(java.lang.String) is not applicable
      (argument mismatch; java.lang.Integer cannot be converted to java.lang.String)

through generics we are getting type safety
at the retreival we are not required to perform typecasting
ArrayList<String> al=new ArrayList<String>();
al.add("aaa");
String s1=al.get(0); // no type-casting needed
conclusion 1: polymorphism concept applicable only for base type and not for parameter type. (usage of parent refernce to hold child object is the concept of polymorphism)
List<String> l=new ArrayList<String>();
ArrayList<String> l=new ArrayList<String>();
Collection<String> l=new ArrayList<String>();

conclusion 2: for type parameter, we can provide any class or interface name but not primitive. if we are trying to provide primitive then we will get a compile-time error.
eg: 
ArrayList<int> al=new ArrayList<int>();
O/P:
Error:(10, 19) java: unexpected type
  required: reference
  found:    int

Generic classes

until 1.4Version a non-generic version of ArrayList class is declared as follows.
class ArrayLiat
{
add(Object o)
Object get(int index)
}
the argument to add method is of Object type and we can add any type of object to the ArrayList. the return type of get method is Object hence at the time of retrieval we have to perform typecasting.

In 1.5V a generic version of ArrayList is declared as follows.
class ArrayList<T> 
{
add(T t)
T get(int index)
}
based on our runtime requirement T will be replaced with our provided type for ex. to hold only String type objects a generic version of ArrayList object can be created as follows. for this requirement compiler considered version  of the ArrayList class is as follows
class ArrayList<String>
{
add(String t)
String get(int index)
}
the argument to add method is String type, if you add any other type then we will get compile-time error. hence through generics, we are getting type safety. the get method returns string hence we are not needed to do typecasting.
in generics we are associating a type parameter to the class, such type of parameterized classes are nothing but generic classes or template classes.

based on our requirement we can define our own generic classes also
class Account<T>{
}
Account<Gold> a1=new Account<Gold>();
Account<Platinum > a1=new Account<Platinum>();

eg:
public class Int {
public static void main(String[] args) {
Gen<Integer> g1=new Gen<Integer>(5);
g1.show();
System.out.println(g1.getOb());
Gen<String> g2=new Gen<String>("ab");
g2.show();
System.out.println(g2.getOb());
Gen<Double> g3=new Gen<Double>(5.44);
g3.show();
System.out.println(g3.getOb());
}
}
class Gen<T>
{
T ob;
Gen(T ob){
this.ob=ob;
}
public void show(){
System.out.println("the type of ob "+ ob.getClass().getName());
}

public T getOb() {
return ob;
}
}
O/P:
the type of ob java.lang.Integer
5
the type of ob java.lang.String
ab
the type of ob java.lang.Double
5.44

Bounded types

we can bound the type parameter for a particular range by using extends keyword such types are called bounded types 
class Test <T> {
}
as a type paramter we can pass any type and there are no restrictions and hence it is unbounded type.
Test <Integer> t1=new Test<Integer>();
Test <String> t1=new Test<String>();

syntax for bounded type:
class Test <T extends X> {
}
X can be either class or interface , if X is a class then we can pass either X type or its child classes . if X is an interface we can pass X type or its implementation classes
public class GenericPract2<T extends Number> {
public static void main(String[] args) {
GenericPract2<Integer> g=new GenericPract2<Integer>();
GenericPract2<String> g1=new GenericPract2<String>();
}
}
Output:
Error:(9, 23) java: type argument java.lang.String is not within bounds of type-variable T


we can bounded types even in combination also
 class GenericPract2<T extends Number & Runnable> {
the pattern should be <T extends CLASS & INTERFACE > but not <T extends INTERFACE  & CLASS> and not  <T extends CLASS & CLASS>
case 1:
public class GenericPract2<T extends Comparable & Number > {
Error:(6, 51) java: interface expected here

case 2:
public class GenericPract2<T extends String & Number > {
Error:(6, 47) java: interface expected here

case 3:
public class GenericPract2<T extends String & Runnable & Comparator> {
valid scenario 


Note: 1)we can define bounded types only by using extends keyword and we cant use implements and super keywords hence we have to replace implements keyword with extends keyword and it serves the same purpose.
2)as a type parameter T we can use any valid java identifier but it is a convention to use T.
3) based on our requirement we can declare any number of type parameters and all these parameters should be separated by comma(,).
class Test<A, B> {
}

No comments:

Post a Comment

SOLID principles in java

SOLID  Single responsibility principle : a class should have only  reason to change, i.e. a class should have only one responsibility/single...