Collection Framework in Java
A Collection allows a group of objects to be treated as a single unit
Java Collection Framework provides a set of utility classes for managing various kinds of collections
The collection interface extends the Iterable interface that specifies an iterator to sequentially access the elements of the Iterable Object
Hierarchy of Map interface
Creating Customized generic List
Now we will create our own Customized generic List which Implements Iterables and behaves the same as Array List
package base;
import java.util.Iterator;
public class OurGenericList<T> implements Iterable<T>{
private T[] items;
private int size;
public OurGenericList() {
size=0;
items=(T[]) new Object[100];
}
public void add(T item) {
items[size++]=item;
}
public T getItemIndex(int index) {
return items[index];
}
public int size() {
return size;
}
@Override
public Iterator iterator() {
// TODO Auto-generated method stub
return new OurGenericIterator(this);
}
private class OurGenericIterator implements Iterator<T>{
private OurGenericList<T> ourGenericList;
private int index=0;
OurGenericIterator( OurGenericList<T> ourGenericList){
this.ourGenericList=ourGenericList;
}
@Override
public boolean hasNext() {
// TODO Auto-generated method stub
return index< ourGenericList.size();
}
@Override
public T next() {
// TODO Auto-generated method stub
return ourGenericList.items[index++];
}
}
}
//Main Class
package base;
import java.util.Iterator;
public class driverclass {
public static void main(String[] args) {
// TODO Auto-generated method stub
OurGenericList<Integer> al=new OurGenericList<>();
al.add(10);
al.add(20);
al.add(30);
al.add(40);
Iterator<Integer> iterator=al.iterator();
while(iterator.hasNext()) {
System.out.println(iterator.next());
}
}
}
The reason why collection extends Iterable is The other classes which extends collection can be Iterable. we can use for each loop to traverse
List :
Lists are collections that maintain their elements in order and can contain duplicates
The elements in List are ordered
Each elements are position based starting from 0
List Methods :
E get(index)
E set(index, element)
void add(int index, element)
boolean AddAll(int index,collection<? extends e> c)
E remove(int index)
Implementations of List
implementations of List are provided in Java.util package
Array List
Linked List
Vector
Array List
Array List is a dynamic array, and will use when we don't know the size
internally it uses some array set to some default size
when the capacity is reached it will create a new Array of bigger sizes (50 % of its current capacity) and copy all the elements from the old array to the new Array
New Array reference for its internal usage
As the old array is no longer in use,and is Garbage collected in the next garbage collection
Vector Class
The vector class is a legacy class that implements Array List
Unlike ArrayList, Vector is thread-safe ie concurrent calls to vector will not compromise its integrity
Linked List
Linked list implements uses doubly linked list
Insertions and deletions are very efficient in a doubly linked list
Array List Vs Linked List
Position-based access has a constant time performance in Array list and Vector class
whereas in Linked List it will take linear time
for insertions and deletion Linked list is better choice when compared to Array List and vector
In addition to List Interface Linked also implements Dequee which help in creating stacks and Queues
Example on ArrayList
import java.util.*;
public class Main
{
public static void main(String[] args) {
System.out.println("Hello World");
List<Integer> al1=new ArrayList<>();
al1.add(10);
al1.add(20);
al1.add(30);
al1.add(40);
List<Integer> al2=new ArrayList<>(al1); // To create a copy of Array List . and we can pass any collection here
al2.add(50);
System.out.println(al1);
System.out.println(al2);
}
}
by using the AddAll method
import java.util.*;
public class Main
{
public static void main(String[] args) {
System.out.println("Hello World");
List<Integer> al1=new ArrayList<>();
al1.add(10);
al1.add(20);
al1.add(30);
al1.add(40);
List<Integer> al2=new ArrayList<>();
al2.add(100);
al2.addAll(al1);
System.out.println(al1);
System.out.println(al2);
System.out.println(al2.indexOf(100)); // To get Index of a particular element
List<Integer> al3=al2.subList(0,4);
System.out.println(al3);
al3.set(0,999);
// System.out.println(al1);
System.out.println(al2);
System.out.println(al3);
}
}
Note: In the above example if we change the subset it will affect the actual Array List
List Iterator :
List Iterator is an Interface that implements Iterator, we can travel in a bidirectional way by using Previous method
next() returns an element and then moves to next position
previous() first moves and then return element
Queues :
The Queue interface extends the collection interface with the following methods
boolean add(E element) // throws illegal state exception if Queue is full
boolean offer(E element) // it does not throw any exception it just return false
E poll() // if queue is empty it will return null
E remove() // If the Queue is empty, it will throw a
NoSuchElement Exception
.
E peek()
Deque :
The Deque interface extends the Queue interface to allow double-ended queues
It allows operations not just at its head , but also at its tail
Elements can be inserted or removed from either end
Adding elements :
boolean offerFirst
boolean offerLast
Void push
void addFirst
void addLast
import java.util.*; public class Main { public static void main(String[] args) { Queue<Integer> q=new LinkedList<>(); q.offer(1); q.offer(2); q.offer(3); System.out.println(q.peek()); //1 System.out.println(q.poll()); //1 System.out.println(q.peek()); //2 } }
Stack Implementation using Deque
Deque<Integer> q=new ArrayDeque<>(); q.addFirst(10); q.addFirst(20); q.addFirst(30); System.out.println(q); System.out.println(q.peek()); System.out.println(q.removeFirst());
Queue implementation using Deque
Deque<Integer> q=new ArrayDeque<>();
q.addFirst(10);
q.addFirst(20);
q.addFirst(30);
System.out.println(q);
System.out.println(q.peek());
System.out.println(q.removeLast());
Priority Queue :
Priority Queue works on priority
The implementation is based on priority heap. a tree like structure that yields an element at the head of the queue according to priority order. which is defined as the natural sorting order of the comparator
In case of several elements having the same priority one of them is choosen arbitrarily
Elements of the priority queue are not sorted
Program to display the top 2 elements from the queue
PriorityQueue<Integer> pq=new PriorityQueue<>();
pq.offer(10);
pq.offer(20);
pq.offer(5);
pq.offer(0);
pq.offer(50);
ArrayList<Integer> al=new ArrayList<>();
int index=0;
while(!pq.isEmpty()){
if(index==2)
break;
al.add(pq.poll());
index++;
}
The above program displays top 2 elements in Ascending order . because PriorityQueue internally implements Comparable which is sorting in ascending order.
The generic type of priority queue must be type of Comparable class other it will class cast exception. Internally integer extends Comparable
if we want to overwrite the behavior pass it as argument to priority queue
class Descending implements Comparator<Integer>
{
public int compare(Interger I1,Integer I2){
if(I1<I2)
return 1;
else if(I1>I2)
return -1;
else
return 0;
}
}
class main{
public static void main(String args[]){
PriorityQueue<Integer> pq=new PriorityQueue<>(new Descending());
pq.offer(10);
pq.offer(20);
pq.offer(5);
pq.offer(0);
pq.offer(50);
ArrayList<Integer> al=new ArrayList<>();
int index=0;
while(!pq.isEmpty()){
if(index==2)
break;
al.add(pq.poll());
index++;
}
}
}
In the above code, we are passing new Descending() as argument hence it will overide natural behavior, and it will sort in Descending Order
The same code can also be written by using lambda expression
PriorityQueue<Integer> pq=new PriorityQueue<>((a,b)->b-a);
By using the above lambda expression we are sorting in Descending order
Total ordering is given priority over Natural Ordering
The same can be applied to customized class . if we want to sort in a particular field
class Student implements Comparable<Student>
{
private int Maths;
private int Physics;
private int chemistry;
public int compareTo(Student o){
return o.physics-this.physics;
}
}
class Descending implements Comparator<Student>
{
public int compare(Student I1,Student I2){
if(I1.getMaths()<I2.getMaths())
return 1;
else if(I1.getMaths()>I2.getMaths())
return -1;
else
return 0;
}
}
class main{
public static void main(String args[]){
PriorityQueue<Integer> pq=new PriorityQueue<>(new Descending());
pq.offer(10);
pq.offer(20);
pq.offer(5);
pq.offer(0);
pq.offer(50);
ArrayList<Integer> al=new ArrayList<>();
int index=0;
while(!pq.isEmpty()){
if(index==2)
break;
al.add(pq.poll());
index++;
}
}
}
In the above example, Natural ordering is based on Physics Marks in Descending order but we have overwritten in Total order by using Maths Marks in Ascending order
Sets :
Set is collection of elements which does not allow duplicates , if we try to add duplicates also it does not print
Set<Integer> hs=new HashSet<>();
hs.add(10);
hs.add(20);
hs.add(30);
hs.add(10);
System.out.println(hs)
hs.remove(20) // it will remove element from the set
// Out put wont contain duplicates
Sets contain following methods :
a.containsAll(b)
a.addAll(b)
a.removeAll(b)
a.retainAll(b)
a.clear()
public static void main(String[] args) {
System.out.println("Hello World");
Set<Integer> hs1=new HashSet<>();
hs1.add(10);
hs1.add(20);
hs1.add(30);
System.out.println(hs1);
Set<Integer> hs2=new HashSet<>();
hs2.add(10);
hs2.add(20);
hs2.add(50);
hs1.retainAll(hs2); // It will give intersection in hs1
System.out.println(hs1);
hs1.addAll(hs2); // It will give Union in hs1
System.out.println(hs1);
}
Linked Hash Set :
The linked Hash set is extension of Hash set. if we traverse the Linked hash set insertion order is maintained
public class Main
{
public static void main(String[] args) {
System.out.println("Hello World");
Set<Integer> hs1=new LinkedHashSet<>();
hs1.add(10);
hs1.add(20);
hs1.add(30);
hs1.add(10)
System.out.println(hs1);
}
}
What if we add Customized objects to hashSet
The basic principle of hashmap is doesn't allow duplicate but if we add same customized object twice it will allow because it has diffrent address, for that we will override Hashcode and equals method
import java.util.*;
class Student
{
int id;
int age;
String name;
Student(int id,int age,String name){
this.id=id;
this.age=age;
this.name=name;
}
public int hashCode(){
return this.id+this.age+this.name.hashCode();
}
public boolean equals(Object s1){
return this.id==((Student)s1).id &&
this.age==((Student)s1).age &&
this.name.hashCode()==((Student)s1).name.hashCode();
}
}
public class Main
{
public static void main(String[] args) {
HashSet<Student> hs=new HashSet<>();
hs.put(new Student(101,23,"Sunil"));
hs.put(new Student(101,23,"Sunil"));
hs.put(new Student(101,23,"Sunil"));
hs.put(new Student(101,23,"Sunil"));
for(Student s:hs){
System.out.println(hs)
}
}
}
Sorted Set :
the sorted set interface extends the set interface to provide the functionality for Handling sorted sets
Since elements are sorted, traversing the set either using the for(:) loop or an iterator will access the elements according to the ordering used by the set
E first()
E last()
Navigable Set Interface :
Extends the SortedSet Interface with navigation methods to find the closest match for specific search targets
pollFirst()
pollLast()
E celing(); // for closest match
E floor();
Tree set :
Tress Set will sort in Ascending order by default , if we want any customized sorting we will pass that in total ordering
Map Interface:
Basically Map stores values in form of Keys and Values,
Hash Map allows one null key , it does not allow duplicates
Tree Map Sorts the records in the ascending order of keys , if we sort in customized order we need to pass in total ordering
We cant iterate through Map as it doesnt iterate Iterable
By using Map.Entry we will process the records
if a Map has customized object as key, then we can insert a duplicate as it has a different base address, Then we will override Model class hasCode and Equals method
Example
import java.util.*;
class Student
{
int id;
int age;
String name;
Student(int id,int age,String name){
this.id=id;
this.age=age;
this.name=name;
}
public int hashCode(){
return this.id+this.age+this.name.hashCode();
}
public boolean equals(Object s1){
return this.id==((Student)s1).id &&
this.age==((Student)s1).age &&
this.name.hashCode()==((Student)s1).name.hashCode();
}
}
public class Main
{
public static void main(String[] args) {
HashMap<Student,Integer> hm=new HashMap<>();
hm.put(new Student(101,23,"Sunil"),1);
hm.put(new Student(101,23,"Sunil"),2);
hm.put(new Student(101,23,"Sunil"),3);
hm.put(new Student(101,23,"Sunil"),4);
System.out.println("The final values in the map is");
for(Map.Entry<Student,Integer> me: hm.entrySet()){
System.out.println(me.getKey().name+" "+me.getKey().age+" "+ me.getKey().id+" "+me.getValue());
}
System.out.println("The final size of the map is");
System.out.println(hm.size());
}
}