Approach Java (3)

Free Online Courses with Certificate
Free Online Courses With Personalized Certificate

Nov 12, 2009

Set and SortedSet

Set, by definition, is a collection of unique elements. In Collections framework, Set is an interface which has 3 general purpose implementations:

One is HashSet, which is most often used. HashSet works like a standard set implementation but without any guarantee of order of elements. That means when we fetch elements back from a HashSet, we may get them in different order than in which we had inserted at first place.

Second is TreeSet, which makes sure that elements are stored according to their natural order (or according to Comparator implementation we provide). When we get them back we get them ordered but of course at performance cost. HashSet operations are faster than TreeSet implementation.

Note – To give little more detail, Set interface is extended by SortedSet interface, which provides few more methods. SortedSet is the interface implemented by TreeSet.

Third is LinkedHashSet, which uses LinkedList logic to store elements. It is essentially HashSet with ordering attached. Elements are retrieved back in same order in which they were inserted. Performance wise, LinkedHashSet is almost similar to HashSet.

There are few more extensions and implementations of Set interface, which I will leave to readers to explore.

Let’s go through some code examples now. As before, make sure to read comments to understand code.


package pkg1;

import java.util.HashSet;
import java.util.Set;

public class Cars {

private Set carSet = new HashSet(5);//not that in constructor we can give initial capacity also although set is auto extended

public static void main(String[ ] args)
{
//add few car objects to carSet
Cars cars = new Cars();
//add method of Set interface adds the object to set ONLY IF IT IS NOT ALREADY PRESENT
cars.getCarSet( ).add(new Car(“Maruti” , 2006));
cars.getCarSet( ).add(new Car(“Indica” , 2008));
cars.getCarSet( ).add(new Car(“Spark”, 2008));
cars.getCarSet( ).add(new Car(“Spark”, 2008));//adding same object again but will be ignored

display(“Car Details”, cars.getCarSet( ));

//remove method removes if car is present else does nothing
cars.getCarSet( ).remove( new Car(“Indica” , 2008));
cars.getCarSet( ).remove( new Car(“Indica” , 2005));

display(“Car Details after remove”, cars.getCarSet( ));

//addAll is equivalent to UNION of sets
Set carSet2 = new HashSet(5);
carSet2.add( new Car(“Maruti” , 2006));
carSet2.add( new Car(“Zen”, 2009));
carSet2.add( new Car(“Santro” , 2005));
cars.getCarSet( ).addAll( carSet2);

display(“Car Details after addAll (UNION)”, cars.getCarSet( ));

//retainAll is equivalent to INTERSECT of sets
carSet2.clear( );//remove all objects from collection
carSet2.add( new Car(“Zen”, 2009));
carSet2.add( new Car(“Santro” , 2005));
carSet2.add( new Car(“Lancer” , 2009));
cars.getCarSet( ).retainAll( carSet2);

display(“Car Details after retainAll (INTERSECT)” , cars.getCarSet( ));

}

private static void display(String message, Set carSet) {
System.out.println( “\n\n”+message) ;
//print size first
System.out.println( “Number of Cars : “+ carSet.size( ));

//when we read back, order is not assured
for (Car car : carSet)
{
System.out.println( car);
}
}

/**
* @return the carSet
*/
public Set<Car> getCarSet() {
return carSet;
}

/**
* @param carSet the carSet to set
*/
public void setCarSet(Set carSet) {
this.carSet = carSet;
}

}

class Car
{
private String carName;
private int carYear;

public Car(String carName, int carYear) {
this.carName = carName;
this.carYear = carYear;
}
/**
* @return the carName
*/
public String getCarName() {
return carName;
}
/**
* @param carName the carName to set
*/
public void setCarName(String carName) {
this.carName = carName;
}
/**
* @return the carYear
*/
public int getCarYear() {
return carYear;
}
/**
* @param carYear the carYear to set
*/
public void setCarYear(int carYear) {
this.carYear = carYear;
}
/* (non-Javadoc)
* @see java.lang.Object# hashCode( )
*/
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((carName == null) ? 0 : carName.hashCode( ));
result = prime * result + carYear;
return result;
}
/* (non-Javadoc)
* @see java.lang.Object# equals(java. lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass( ))
return false;
final Car other = (Car) obj;
if (carName == null) {
if (other.carName != null)
return false;
} else if (!carName.equals( other.carName) )
return false;
if (carYear != other.carYear)
return false;
return true;
}
/* (non-Javadoc)
* @see java.lang.Object# toString( )
*/
@Override
public String toString() {
// TODO Auto-generated method stub
return “Car Name = “+this.getCarName( ) + ” , Car Year = “+this.getCarYear( );
}

}

Running CarSet class gives following output:


Car Details
Number of Cars : 3
Car Name = Indica , Car Year = 2008
Car Name = Maruti , Car Year = 2006
Car Name = Spark , Car Year = 2008

Car Details after remove
Number of Cars : 2
Car Name = Maruti , Car Year = 2006
Car Name = Spark , Car Year = 2008

Car Details after addAll (UNION)
Number of Cars : 4
Car Name = Santro , Car Year = 2005
Car Name = Maruti , Car Year = 2006
Car Name = Spark , Car Year = 2008
Car Name = Zen , Car Year = 2009

Car Details after retainAll (INTERSECT)
Number of Cars : 2
Car Name = Santro , Car Year = 2005
Car Name = Zen , Car Year = 2009

You can see the working of some common Set methods. You can also see that elements are not printed in same order as they were inserted.

Now make a very small change. In Cars.java, change HashSet to LinkedHashSet. Compile and run again, now we get following output:


Car Details
Number of Cars : 3
Car Name = Maruti , Car Year = 2006
Car Name = Indica , Car Year = 2008
Car Name = Spark , Car Year = 2008

Car Details after remove
Number of Cars : 2
Car Name = Maruti , Car Year = 2006
Car Name = Spark , Car Year = 2008

Car Details after addAll (UNION)
Number of Cars : 4
Car Name = Maruti , Car Year = 2006
Car Name = Spark , Car Year = 2008
Car Name = Zen , Car Year = 2009
Car Name = Santro , Car Year = 2005

Car Details after retainAll (INTERSECT)
Number of Cars : 2
Car Name = Zen , Car Year = 2009
Car Name = Santro , Car Year = 2005

You can see, insertion order is remembered while retrieving elements back.


Note – You also see the power of abstraction – just by changing HashSet to LinkedHashSet, an entire different implementation is in use.

We will now change the class to use TreeSet. But this needs some effort because TreeSet also needs logic to compare 2 objects to sort them. This means either our Car class needs to implement Comparable interface and implement compareTo method or we need to pass a Comparator implementation at the time of calling constructor. I will give an example of former. Second method, I leave to you as an exercise. Once again, go through the code and comments to understand TreeSet:


package pkg1;

import java.util.Set;
import java.util.TreeSet;

public class OrderedCars {

private Set carSet = new TreeSet();//not that TreeSet constructor cannot take initial capacity, not applicable here

public static void main(String[ ] args)
{
//add few car objects to carSet
OrderedCars cars = new OrderedCars( );
//add method of Set interface adds the object to set ONLY IF IT IS NOT ALREADY PRESENT
cars.getCarSet( ).add(new Car(“Maruti” , 2006));
cars.getCarSet( ).add(new Car(“Indica” , 2008));
cars.getCarSet( ).add(new Car(“Spark”, 2008));
cars.getCarSet( ).add(new Car(“Indica” , 2005));

display(“Car Details”, cars.getCarSet( ));

//remove method removes if car is present else does nothing
cars.getCarSet( ).remove( new Car(“Indica” , 2008));
cars.getCarSet( ).remove( new Car(“Indica” , 2005));

display(“Car Details after remove”, cars.getCarSet( ));

//addAll is equivalent to UNION of sets
Set<Car> carSet2 = new TreeSet<Car>();
carSet2.add( new Car(“Maruti” , 2006));
carSet2.add( new Car(“Zen”, 2009));
carSet2.add( new Car(“Santro” , 2005));
cars.getCarSet( ).addAll( carSet2);

display(“Car Details after addAll (UNION)”, cars.getCarSet( ));

//retainAll is equivalent to INTERSECT of sets
carSet2.clear( );//remove all objects from collection
carSet2.add( new Car(“Zen”, 2009));
carSet2.add( new Car(“Santro” , 2005));
carSet2.add( new Car(“Lancer” , 2009));
cars.getCarSet( ).retainAll( carSet2);

display(“Car Details after retainAll (INTERSECT)” , cars.getCarSet( ));

}

private static void display(String message, Set<Car> carSet) {
System.out.println( “\n\n”+message) ;
//print size first
System.out.println( “Number of Cars : “+ carSet.size( ));

//when we read back, order is not assured
for (Car car : carSet)
{
System.out.println( car);
}
}

/**
* @return the carSet
*/
public Set<Car> getCarSet() {
return carSet;
}

/**
* @param carSet the carSet to set
*/
public void setCarSet(Set carSet) {
this.carSet = carSet;
}

}

class Car implements Comparable<Car>
{
private String carName;
private int carYear;

public Car(String carName, int carYear) {
this.carName = carName;
this.carYear = carYear;
}
/**
* @return the carName
*/
public String getCarName() {
return carName;
}
/**
* @param carName the carName to set
*/
public void setCarName(String carName) {
this.carName = carName;
}
/**
* @return the carYear
*/
public int getCarYear() {
return carYear;
}
/**
* @param carYear the carYear to set
*/
public void setCarYear(int carYear) {
this.carYear = carYear;
}
/* (non-Javadoc)
* @see java.lang.Object# hashCode( )
*/
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((carName == null) ? 0 : carName.hashCode( ));
result = prime * result + carYear;
return result;
}
/* (non-Javadoc)
* @see java.lang.Object# equals(java. lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass( ))
return false;
final Car other = (Car) obj;
if (carName == null) {
if (other.carName != null)
return false;
} else if (!carName.equals( other.carName) )
return false;
if (carYear != other.carYear)
return false;
return true;
}
/* (non-Javadoc)
* @see java.lang.Object# toString( )
*/
@Override
public String toString() {
// TODO Auto-generated method stub
return “Car Name = “+this.getCarName( ) + ” , Car Year = “+this.getCarYear( );
}
public int compareTo(Car car2) {
if (this.equals( car2)) return 0;

if (this.getCarName( ).compareTo( car2.getCarName( )) == 0)
{
if (this.getCarYear( ) > car2.getCarYear( ))
{
return 1;
}
else
{
return -1;
}
}
else
{
return this.getCarName( ).compareTo( car2.getCarName( ));
}
}

}

Here’s the output with TreeSet implementation:


Car Details
Number of Cars : 4
Car Name = Indica , Car Year = 2005
Car Name = Indica , Car Year = 2008
Car Name = Maruti , Car Year = 2006
Car Name = Spark , Car Year = 2008

Car Details after remove
Number of Cars : 2
Car Name = Maruti , Car Year = 2006
Car Name = Spark , Car Year = 2008

Car Details after addAll (UNION)
Number of Cars : 4
Car Name = Maruti , Car Year = 2006
Car Name = Santro , Car Year = 2005
Car Name = Spark , Car Year = 2008
Car Name = Zen , Car Year = 2009

Car Details after retainAll (INTERSECT)
Number of Cars : 2
Car Name = Santro , Car Year = 2005
Car Name = Zen , Car Year = 2009

Make sure you take a look at compareTo method to see how cars are compared internally by TreeSet implementation to sort them. Also look at output and you will find 2 Indicas placed at the top clubbed together, although they were inserted in different order.


Note: Instead of Car class, if we use String class objects or any primitive wrapper objects, we will not need to implement compareTo method, because in that case natural ordering is user by default.

After Set and SortedSet, we are left with following under Collections lesson:

Map and SortedMap.

We will cover these in next lesson. Questions, comments and corrections are always welcome.

Nov 18, 2009

Map and SortedMap

In this lesson we will learn about Maps and SortedMaps. Maps are structures to store key-value pairs. Keys are always unique in a Map, but values can be duplicate. In Java, Map is an interface, for which several implementations are provided. SortedMap is also an interface that extends Map interface. It adds order to the Map as we will see below.

Both key and value must be objects in Java Maps.

Note : In fact, if you have observed in collections lesson, we have dealt with objects only. Primitive types are not directly stored in collections, but autoboxing has eased the task of conversion. Autoboxing is a feature added in Java 1.5 to auto convert primitives into their respective wrapper class instances and vice versa.

Just like Sets, there are several implementations of Map interface, but we will cover most common ones, which are:

HashMap – no guarantee of retrieval order
LinkedHashMap – retrieval order is same as insertion order
TreeMap – Elements are stored according to natural order of keys, or according to Comparator object provided by programmer at the time of calling constructor

Another class which is very useful to learn about under Maps is Map.Entry. A single Map.Entry object basically represents single key-value pair of Maps. Going through code will make it clear.

Let’s go through the code. I have tried to depict the usage of most common methods under Maps. Make sure you read comments for better understanding.


package pkg1;

import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

public class StudentCourses {

private Map studentCourseMap = new HashMap(5);

public static void main(String[] args)
{
// add few student-course pairs
StudentCourses studentCourses = new StudentCourses();
// put method of Map interface stores the key-value pair
//if the key is ALREADY PRESENT, object is OVER-WRITTEN
Student sanjay = new Student(“Sanjay”, 101);
Student rony = new Student(“Rony”, 102);
Student ritika = new Student(“Ritika”, 103);
Student malti = new Student(“Malti”, 104);
Student leslie = new Student(“Leslie”, 105);

Course java = new Course(“Java”, 3);
Course cpp = new Course(“CPP”, 5);
Course dotNet = new Course(“DotNet”, 6);

studentCourses.getStudentCourseMap().put(sanjay, java);
studentCourses.getStudentCourseMap().put(sanjay, cpp);//SINCE sanjay key already exists, java is overwritten with cpp
studentCourses.getStudentCourseMap().put(rony, dotNet);
studentCourses.getStudentCourseMap().put(malti, cpp);
studentCourses.getStudentCourseMap().put(ritika, dotNet);
studentCourses.getStudentCourseMap().put(leslie, java);

display(“Student Course Details”, studentCourses.getStudentCourseMap());

// remove method removes if KEY is present else does nothing
studentCourses.getStudentCourseMap().remove(ritika);
Student aarti = new Student(“Aarti”, 120);
studentCourses.getStudentCourseMap().remove(aarti);

display(“Student Course Details after removing “+ritika+” and “+aarti, studentCourses.getStudentCourseMap());

// addAll is equivalent to UNION of Maps
Map studentCourseMap2 = new LinkedHashMap(5);
studentCourseMap2.put(aarti, java);
studentCourseMap2.put(malti, dotNet);

studentCourses.getStudentCourseMap().putAll(studentCourseMap2);

display(“Student Course Details after addAll (UNION) of another map containing “+aarti+ ” and “+ malti, studentCourses.getStudentCourseMap());
System.out.println(“Note above that Malti’s course is changed to that in new Map”);

//MORE general purpose methods
System.out.println(“\n\nMORE GENERAL PURPOSE METHODS “);
System.out.println(“Size of Map = ” + studentCourses.getStudentCourseMap().size());
System.out.println(“Does Student “+new Student(“JaSON”,112)+” exist in keys = ” + studentCourses.getStudentCourseMap().containsKey(new Student(“JaSON”,112)));
System.out.println(“Does Course “+new Course(“DotNet”,6)+” exist in values = ” + studentCourses.getStudentCourseMap().containsValue(new Course(“DotNet”,6)));
System.out.println(“Is map empty= ” + studentCourses.getStudentCourseMap().isEmpty());
System.out.println(“Keys of Map = ” + studentCourses.getStudentCourseMap().keySet());
System.out.println(“Values of Map = ” + studentCourses.getStudentCourseMap().values());
}

private static void display(String message, Map studentCourseMap) {
System.out.println(“\n\n”+message);
// print size first
System.out.println(“Number of Records : “+ studentCourseMap.size());

// when we read back, order is not assured
// note here that elements are retrieved one by one in Map.Entry<key,value> objects, and
//this object has methods to get key and value separately
//studentCourseMap.entrySet() method returns the Map.Entry object collection
for (Map.Entry studentCourse : studentCourseMap.entrySet())
{
System.out.println(studentCourse.getKey() + ” HAS TAKEN ADMISSION IN ” + studentCourse.getValue());
}
}

/**
* @return the studentCourseMap
*/
public Map<Student,Course> getStudentCourseMap() {
return studentCourseMap;
}

/**
* @param studentCourseMap
* the studentCourseMap to set
*/
public void setStudentCourseMap(Map studentCourseMap) {
this.studentCourseMap = studentCourseMap;
}

}

class Student
{
private String studentName;
private int admissionNumber;

public Student(String studentName, int admNumber) {
this.studentName = studentName;
this.admissionNumber = admNumber;
}


/**
* @return the studentName
*/
public String getStudentName() {
return studentName;
}
/**
* @param studentName
* the studentName to set
*/
public void setStudentName(String studentName) {
this.studentName = studentName;
}
/**
* @return the admissionNumber
*/
public int getAdmissionNumber() {
return admissionNumber;
}
/**
* @param admissionNumber
* the admissionNumber to set
*/
public void setStudentYear(int admissionNumber) {
this.admissionNumber = admissionNumber;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((studentName == null) ? 0 : studentName.hashCode());
result = prime * result + admissionNumber;
return result;
}

/*
* (non-Javadoc)
*
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
final Student other = (Student) obj;
if (studentName == null) {
if (other.studentName != null)
return false;
} else if (!studentName.equals(other.studentName))
return false;
if (admissionNumber != other.admissionNumber)
return false;
return true;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
// TODO Auto-generated method stub
return “Student Name = “+this.getStudentName() + ” , Admission Number = “+this.getAdmissionNumber();
}
}

class Course
{
private String courseName;
private int durationInMonths;

public Course(String courseName, int duration) {
this.courseName = courseName;
this.durationInMonths = duration;
}

/**
* @return the courseName
*/
public String getCourseName() {
return courseName;
}
/**
* @param courseName
* the courseName to set
*/
public void setCourseName(String courseName) {
this.courseName = courseName;
}
/**
* @return the durationInMonths
*/
public int getDuration() {
return durationInMonths;
}
/**
* @param durationInMonths
* the durationInMonths to set
*/
public void setCourseYear(int durationInMonths) {
this.durationInMonths = durationInMonths;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((courseName == null) ? 0 : courseName.hashCode());
result = prime * result + durationInMonths;
return result;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
final Course other = (Course) obj;
if (courseName == null) {
if (other.courseName != null)
return false;
} else if (!courseName.equals(other.courseName))
return false;
if (durationInMonths != other.durationInMonths)
return false;
return true;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
// TODO Auto-generated method stub
return “Course Name = “+this.getCourseName() + ” , Duration in Months = “+this.getDuration();
}
}

Copy and save all of above code in a single file StudentCourses.java. If your package name is different, change it at package statement.

If you go through comments in code, it should be clear to you. Output on running above class is this at my end:


Student Course Details
Number of Records : 5
Student Name = Malti , Admission Number = 104 HAS TAKEN ADMISSION IN Course Name = CPP , Duration in Months = 5
Student Name = Sanjay , Admission Number = 101 HAS TAKEN ADMISSION IN Course Name = CPP , Duration in Months = 5
Student Name = Rony , Admission Number = 102 HAS TAKEN ADMISSION IN Course Name = DotNet , Duration in Months = 6
Student Name = Leslie , Admission Number = 105 HAS TAKEN ADMISSION IN Course Name = Java , Duration in Months = 3
Student Name = Ritika , Admission Number = 103 HAS TAKEN ADMISSION IN Course Name = DotNet , Duration in Months = 6

Student Course Details after removing Student Name = Ritika , Admission Number = 103 and Student Name = Aarti , Admission Number = 120
Number of Records : 4
Student Name = Malti , Admission Number = 104 HAS TAKEN ADMISSION IN Course Name = CPP , Duration in Months = 5
Student Name = Sanjay , Admission Number = 101 HAS TAKEN ADMISSION IN Course Name = CPP , Duration in Months = 5
Student Name = Rony , Admission Number = 102 HAS TAKEN ADMISSION IN Course Name = DotNet , Duration in Months = 6
Student Name = Leslie , Admission Number = 105 HAS TAKEN ADMISSION IN Course Name = Java , Duration in Months = 3

Student Course Details after addAll (UNION) of another map containing Student Name = Aarti , Admission Number = 120 and Student Name = Malti , Admission Number = 104
Number of Records : 5
Student Name = Aarti , Admission Number = 120 HAS TAKEN ADMISSION IN Course Name = Java , Duration in Months = 3
Student Name = Malti , Admission Number = 104 HAS TAKEN ADMISSION IN Course Name = DotNet , Duration in Months = 6
Student Name = Sanjay , Admission Number = 101 HAS TAKEN ADMISSION IN Course Name = CPP , Duration in Months = 5
Student Name = Rony , Admission Number = 102 HAS TAKEN ADMISSION IN Course Name = DotNet , Duration in Months = 6
Student Name = Leslie , Admission Number = 105 HAS TAKEN ADMISSION IN Course Name = Java , Duration in Months = 3
Note above that Malti’s course is changed to that in new Map

MORE GENERAL PURPOSE METHODS
Size of Map = 5
Does Student Student Name = JaSON , Admission Number = 112 exist in keys = false
Does Course Course Name = DotNet , Duration in Months = 6 exist in values = true
Is map empty= false
Keys of Map = [Student Name = Aarti , Admission Number = 120, Student Name = Malti , Admission Number = 104, Student Name = Sanjay , Admission Number = 101, Student Name = Rony , Admission Number = 102, Student Name = Leslie , Admission Number = 105]
Values of Map = [Course Name = Java , Duration in Months = 3, Course Name = DotNet , Duration in Months = 6, Course Name = CPP , Duration in Months = 5, Course Name = DotNet , Duration in Months = 6, Course Name = Java , Duration in Months = 3]

Note that records are not returned/printed in same order in which they were added. This is because of using HashMap which does not give any guarantee about retrieval order.

As we did with Sets, just by changing HashMap to LinkedHashMap in above code we will get LinkedHashMap’s implementation.

Change


private Map studentCourseMap = new HashMap(5);

To

private Map studentCourseMap = new LinkedHashMap(5);

This is the output we get on doing this:

Student Course Details
Number of Records : 5
Student Name = Sanjay , Admission Number = 101 HAS TAKEN ADMISSION IN Course Name = CPP , Duration in Months = 5
Student Name = Rony , Admission Number = 102 HAS TAKEN ADMISSION IN Course Name = DotNet , Duration in Months = 6
Student Name = Malti , Admission Number = 104 HAS TAKEN ADMISSION IN Course Name = CPP , Duration in Months = 5
Student Name = Ritika , Admission Number = 103 HAS TAKEN ADMISSION IN Course Name = DotNet , Duration in Months = 6
Student Name = Leslie , Admission Number = 105 HAS TAKEN ADMISSION IN Course Name = Java , Duration in Months = 3

Student Course Details after removing Student Name = Ritika , Admission Number = 103 and Student Name = Aarti , Admission Number = 120
Number of Records : 4
Student Name = Sanjay , Admission Number = 101 HAS TAKEN ADMISSION IN Course Name = CPP , Duration in Months = 5
Student Name = Rony , Admission Number = 102 HAS TAKEN ADMISSION IN Course Name = DotNet , Duration in Months = 6
Student Name = Malti , Admission Number = 104 HAS TAKEN ADMISSION IN Course Name = CPP , Duration in Months = 5
Student Name = Leslie , Admission Number = 105 HAS TAKEN ADMISSION IN Course Name = Java , Duration in Months = 3

Student Course Details after addAll (UNION) of another map containing Student Name = Aarti , Admission Number = 120 and Student Name = Malti , Admission Number = 104
Number of Records : 5
Student Name = Sanjay , Admission Number = 101 HAS TAKEN ADMISSION IN Course Name = CPP , Duration in Months = 5
Student Name = Rony , Admission Number = 102 HAS TAKEN ADMISSION IN Course Name = DotNet , Duration in Months = 6
Student Name = Malti , Admission Number = 104 HAS TAKEN ADMISSION IN Course Name = DotNet , Duration in Months = 6
Student Name = Leslie , Admission Number = 105 HAS TAKEN ADMISSION IN Course Name = Java , Duration in Months = 3
Student Name = Aarti , Admission Number = 120 HAS TAKEN ADMISSION IN Course Name = Java , Duration in Months = 3
Note above that Malti’s course is changed to that in new Map

MORE GENERAL PURPOSE METHODS
Size of Map = 5
Does Student Student Name = JaSON , Admission Number = 112 exist in keys = false
Does Course Course Name = DotNet , Duration in Months = 6 exist in values = true
Is map empty= false
Keys of Map = [Student Name = Sanjay , Admission Number = 101, Student Name = Rony , Admission Number = 102, Student Name = Malti , Admission Number = 104, Student Name = Leslie , Admission Number = 105, Student Name = Aarti , Admission Number = 120]
Values of Map = [Course Name = CPP , Duration in Months = 5, Course Name = DotNet , Duration in Months = 6, Course Name = DotNet , Duration in Months = 6, Course Name = Java , Duration in Months = 3, Course Name = Java , Duration in Months = 3]

Now the retrieval order is same as insertion order.

SortedMap

Let’s now try to store and retrieve objects according to our defined sort order of KEYS (ordering is based on keys only). Suppose we want that objects be stored according to admissionNumber in descending order, hence we should get them back in same order on retrieval. For this, as before, either we need Student class to implement Comparable interface and implement compareTo method, or we need to pass a Comparator object to Map construtor. First method was explained under Sets lesson. In this lesson I will use second method. Note that we will use TreeMap implementation of sets, which supports ordered storage and retrieval.

Look at this code and go through comments:


package pkg1;

import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.TreeMap;

public class SortedStudentCourses {

//THIS IS HOW WE DEFINE COMPARATOR OBJECT
//basically implement Comparator interface and its compare method
private static Comparator studentComparator = new Comparator(){
public int compare(Student s1, Student s2)
{
int result = 0;
//usually if object 1 > object 2, +1 is returned, but since we want
//descending order, hence -1 is returned
if (s1.getAdmissionNumber() > s2.getAdmissionNumber())
result = -1;
else if (s1.getAdmissionNumber() < s2.getAdmissionNumber())
result = 1;

return result;
}
};

private Map studentCourseMap = new TreeMap(studentComparator);

public static void main(String[] args)
{
// add few student-course pairs
SortedStudentCourses studentCourses = new SortedStudentCourses();
// put method of Map interface stores the key-value pair
//if the key is ALREADY PRESENT, object is OVER-WRITTEN
Student sanjay = new Student(“Sanjay”, 101);
Student rony = new Student(“Rony”, 102);
Student ritika = new Student(“Ritika”, 103);
Student malti = new Student(“Malti”, 104);
Student leslie = new Student(“Leslie”, 105);

Course java = new Course(“Java”, 3);
Course cpp = new Course(“CPP”, 5);
Course dotNet = new Course(“DotNet”, 6);

studentCourses.getStudentCourseMap().put(sanjay, java);
studentCourses.getStudentCourseMap().put(sanjay, cpp);//SINCE sanjay key already exists, java is overwritten with cpp
studentCourses.getStudentCourseMap().put(rony, dotNet);
studentCourses.getStudentCourseMap().put(malti, cpp);
studentCourses.getStudentCourseMap().put(ritika, dotNet);
studentCourses.getStudentCourseMap().put(leslie, java);

display(“Student Course Details”, studentCourses.getStudentCourseMap());

// remove method removes if KEY is present else does nothing
studentCourses.getStudentCourseMap().remove(ritika);
Student aarti = new Student(“Aarti”, 120);
studentCourses.getStudentCourseMap().remove(aarti);

display(“Student Course Details after removing “+ritika+” and “+aarti, studentCourses.getStudentCourseMap());

// addAll is equivalent to UNION of Maps
Map studentCourseMap2 = new LinkedHashMap(5);
studentCourseMap2.put(aarti, java);
studentCourseMap2.put(malti, dotNet);

studentCourses.getStudentCourseMap().putAll(studentCourseMap2);

display(“Student Course Details after addAll (UNION) of another map containing “+aarti+ ” and “+ malti, studentCourses.getStudentCourseMap());
System.out.println(“Note above that Malti’s course is changed to that in new Map”);

//MORE general purpose methods
System.out.println(“\n\nMORE GENERAL PURPOSE METHODS “);
System.out.println(“Size of Map = ” + studentCourses.getStudentCourseMap().size());
System.out.println(“Does Student “+new Student(“JaSON”,112)+” exist in keys = ” + studentCourses.getStudentCourseMap().containsKey(new Student(“JaSON”,112)));
System.out.println(“Does Course “+new Course(“DotNet”,6)+” exist in values = ” + studentCourses.getStudentCourseMap().containsValue(new Course(“DotNet”,6)));
System.out.println(“Is map empty= ” + studentCourses.getStudentCourseMap().isEmpty());
System.out.println(“Keys of Map = ” + studentCourses.getStudentCourseMap().keySet());
System.out.println(“Values of Map = ” + studentCourses.getStudentCourseMap().values());
}

private static void display(String message, Map studentCourseMap) {
System.out.println(“\n\n”+message);
// print size first
System.out.println(“Number of Records : “+ studentCourseMap.size());

// when we read back, order is descending according to admissionNumber
// note here that elements are retrieved one by one in Map.Entry objects, and
//this object has methods to get key and value separately
//studentCourseMap.entrySet() method returns the Map.Entry object collection
for (Map.Entry studentCourse : studentCourseMap.entrySet())
{
System.out.println(studentCourse.getKey() + ” HAS TAKEN ADMISSION IN ” + studentCourse.getValue());
}
}

/**
* @return the studentCourseMap
*/
public Map getStudentCourseMap() {
return studentCourseMap;
}

/**
* @param studentCourseMap
* the studentCourseMap to set
*/
public void setStudentCourseMap(Map studentCourseMap) {
this.studentCourseMap = studentCourseMap;
}
}

class Student
{
private String studentName;
private int admissionNumber;

public Student(String studentName, int admNumber) {
this.studentName = studentName;
this.admissionNumber = admNumber;
}

/**
* @return the studentName
*/
public String getStudentName() {
return studentName;
}
/**
* @param studentName
* the studentName to set
*/
public void setStudentName(String studentName) {
this.studentName = studentName;
}
/**
* @return the admissionNumber
*/
public int getAdmissionNumber() {
return admissionNumber;
}
/**
* @param admissionNumber
* the admissionNumber to set
*/
public void setStudentYear(int admissionNumber) {
this.admissionNumber = admissionNumber;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((studentName == null) ? 0 : studentName.hashCode());
result = prime * result + admissionNumber;
return result;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
final Student other = (Student) obj;
if (studentName == null) {
if (other.studentName != null)
return false;
} else if (!studentName.equals(other.studentName))
return false;
if (admissionNumber != other.admissionNumber)
return false;
return true;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
// TODO Auto-generated method stub
return “Student Name = “+this.getStudentName() + ” , Admission Number = “+this.getAdmissionNumber();
}
}

class Course
{
private String courseName;
private int durationInMonths;

public Course(String courseName, int duration) {
this.courseName = courseName;
this.durationInMonths = duration;
}

/**
* @return the courseName
*/
public String getCourseName() {
return courseName;
}
/**
* @param courseName
* the courseName to set
*/
public void setCourseName(String courseName) {
this.courseName = courseName;
}
/**
* @return the durationInMonths
*/
public int getDuration() {
return durationInMonths;
}
/**
* @param durationInMonths
* the durationInMonths to set
*/
public void setCourseYear(int durationInMonths) {
this.durationInMonths = durationInMonths;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((courseName == null) ? 0 : courseName.hashCode());
result = prime * result + durationInMonths;
return result;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
final Course other = (Course) obj;
if (courseName == null) {
if (other.courseName != null)
return false;
} else if (!courseName.equals(other.courseName))
return false;
if (durationInMonths != other.durationInMonths)
return false;
return true;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
// TODO Auto-generated method stub
return “Course Name = “+this.getCourseName() + ” , Duration in Months = “+this.getDuration();
}
}

Interesting code change to look at is how we defined a comparator object to sort students at the beginning.

Here’s the output:


Student Course Details
Number of Records : 5
Student Name = Leslie , Admission Number = 105 HAS TAKEN ADMISSION IN Course Name = Java , Duration in Months = 3
Student Name = Malti , Admission Number = 104 HAS TAKEN ADMISSION IN Course Name = CPP , Duration in Months = 5
Student Name = Ritika , Admission Number = 103 HAS TAKEN ADMISSION IN Course Name = DotNet , Duration in Months = 6
Student Name = Rony , Admission Number = 102 HAS TAKEN ADMISSION IN Course Name = DotNet , Duration in Months = 6
Student Name = Sanjay , Admission Number = 101 HAS TAKEN ADMISSION IN Course Name = CPP , Duration in Months = 5

Student Course Details after removing Student Name = Ritika , Admission Number = 103 and Student Name = Aarti , Admission Number = 120
Number of Records : 4
Student Name = Leslie , Admission Number = 105 HAS TAKEN ADMISSION IN Course Name = Java , Duration in Months = 3
Student Name = Malti , Admission Number = 104 HAS TAKEN ADMISSION IN Course Name = CPP , Duration in Months = 5
Student Name = Rony , Admission Number = 102 HAS TAKEN ADMISSION IN Course Name = DotNet , Duration in Months = 6
Student Name = Sanjay , Admission Number = 101 HAS TAKEN ADMISSION IN Course Name = CPP , Duration in Months = 5

Student Course Details after addAll (UNION) of another map containing Student Name = Aarti , Admission Number = 120 and Student Name = Malti , Admission Number = 104
Number of Records : 5
Student Name = Aarti , Admission Number = 120 HAS TAKEN ADMISSION IN Course Name = Java , Duration in Months = 3
Student Name = Leslie , Admission Number = 105 HAS TAKEN ADMISSION IN Course Name = Java , Duration in Months = 3
Student Name = Malti , Admission Number = 104 HAS TAKEN ADMISSION IN Course Name = DotNet , Duration in Months = 6
Student Name = Rony , Admission Number = 102 HAS TAKEN ADMISSION IN Course Name = DotNet , Duration in Months = 6
Student Name = Sanjay , Admission Number = 101 HAS TAKEN ADMISSION IN Course Name = CPP , Duration in Months = 5
Note above that Malti’s course is changed to that in new Map

MORE GENERAL PURPOSE METHODS
Size of Map = 5
Does Student Student Name = JaSON , Admission Number = 112 exist in keys = false
Does Course Course Name = DotNet , Duration in Months = 6 exist in values = true
Is map empty= false
Keys of Map = [Student Name = Aarti , Admission Number = 120, Student Name = Leslie , Admission Number = 105, Student Name = Malti , Admission Number = 104, Student Name = Rony , Admission Number = 102, Student Name = Sanjay , Admission Number = 101]
Values of Map = [Course Name = Java , Duration in Months = 3, Course Name = Java , Duration in Months = 3, Course Name = DotNet , Duration in Months = 6, Course Name = DotNet , Duration in Months = 6, Course Name = CPP , Duration in Months = 5]

Look at admission numbers of printed records, descending order, as we expected!!

Before we end this lesson (and also this chapter, yey!!), there’s another trick/mistake programmers can commit, that I would like to share. In compare method in previous example, a small mistake can lead to unexpected behavior in Map. Suppose I change the compare method to this:


public int compare(Student s1, Student s2)
{
int result = 0;
//usually if object 1 > object 2, +1 is returned, but since we want
//descending order, hence -1 is returned
if (s1.getAdmissionNumber() > s2.getAdmissionNumber())
result = -1;
else
result = 1;

return result;
}

Now on running the same program, first block of output is this (I have ommitted other blocks of output to keep it short):

Student Course Details

Number of Records : 6
Student Name = Leslie , Admission Number = 105 HAS TAKEN ADMISSION IN Course Name = Java , Duration in Months = 3
Student Name = Malti , Admission Number = 104 HAS TAKEN ADMISSION IN Course Name = CPP , Duration in Months = 5
Student Name = Ritika , Admission Number = 103 HAS TAKEN ADMISSION IN Course Name = DotNet , Duration in Months = 6
Student Name = Rony , Admission Number = 102 HAS TAKEN ADMISSION IN Course Name = DotNet , Duration in Months = 6
Student Name = Sanjay , Admission Number = 101 HAS TAKEN ADMISSION IN Course Name = Java , Duration in Months = 3
Student Name = Sanjay , Admission Number = 101 HAS TAKEN ADMISSION IN Course Name = CPP , Duration in Months = 5

Strange thing is Sanjay appears twice. Didn’t we say keys cannot be duplicate in a Map?

Problem is in compare method above. Map is calling compare method before inserting every new pair into map. Since compare method is never returning 0 (observe that it is always returning 1 or -1), hence Map implementation does not know 2 Sanjay objects are equal (i.e. they have same key). Hence it duplicates them. Make sure your compare or compareTo methods are correctly coded whenever you use them in Collections.

This is last topic under Collections chapter. We are done with this long chapter finally. Your comments, questions and corrections are most welcome. I will start with next chapter soon.


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.