How compareTo() method work ArrayList Sorting - java

I'm new to Java and try to learn Java collections and try to sort arraylist with comparable interface. I follow some tutorials and I'm unable to understand what is happen within the compareto() method here. this is my code.
Student.java
package arraylistexample;
public class Student implements Comparable<Student>{
private String studentName;
private int age;
private int rollno;
public Student(String studentName, int age, int rollno){
this.studentName=studentName;
this.age=age;
this.rollno=rollno;
}
public String getStudent(){
return studentName;
}
public int getAge(){
return age;
}
public int getRollno(){
return rollno;
}
public void setStudent(String Student){
studentName=Student;
}
public void setAge(int age){
this.age=age;
}
public void setRollno(int rollno){
this.rollno=rollno;
}
public int compareTo(Student compares) {
int compareage=((Student)compares).getAge();
/* For Ascending order*/
return this.age-compareage;
}
public String toString() {
return "[ rollno=" + rollno + ", name=" + studentName + ", age=" + age + "]";
}
}
ArrayListSorting.java
package arraylistexample;
import java.util.*;
public class ArrayListSorting {
public static void main(String[] args){
ArrayList<Student> obj=new ArrayList<Student>();
obj.add(new Student("Peter", 27,1));
obj.add(new Student("John",26,7));
obj.add(new Student("Jack",21,5));
Collections.sort(obj);
for(Student str:obj){
System.out.println(str);
}
}
}
The problem is I can't understand how caompareto() method works in here. I googled and read many tutorials. But didn't get clear idea. Can anyone help me.

How compareTo works
If the two elements (a,b) being compared are already in the right order, a.compareTo(b) will return a value that is <= 0, so nothing has to happen.
If they aren't in the right order, the return value is > 0, indicating that they must be interchanged.
So in your case student object passed in your compareTo method whose age is greater than your reference object (this) student age they get interchanged to default sorting which is ascending.

Writing a compareTo method for your class lets you specify what criteria your program will use to decide which of two objects of that class should come first in order.
If you don't write a compareTo method for your class, then your program has no way of knowing which order to put two objects in - and therefore it has no way of sorting a whole lot of objects.
But if you write a compareTo method in your class, AND indicate that your class implements the Comparable interface, then your program will be able to sort any number of objects of that class.
What that means is that you have to decide what order you want your Student objects to appear in. Maybe you want them sorted by roll number. So you write your compareTo accordingly, like this.
public int compareTo(Student other) {
return rollno - other.rollno;
}
This particular method will return
a positive number if the current student has a higher roll number than the student called other,
a negative number if the current student has a lower roll number than the student called other.
zero if you try to compare a student to itself.
So it meets all the criteria that a compareTo method has to meet; and it can be used to sort a bunch of students. The actual algorithm that's used for the sort is buried in the Collections.sort method. You don't need to know what it is - you only need to know that it uses your compareTo method in the course of doing the sort.

From Oracle docs of the compareTo method:
Compares this object with the specified object for order. Returns a negative integer, zero, or a positive integer as this object is less than, equal to, or greater than the specified object.
When you implement the method for your own composite-object, you are creating a way to compare those objects.
For example:
A Student named "Peter" and a 'Student named "Greg" - who is bigger/smaller?
That's up to you to decide... You can either choose alphabetical order of name, or ages, or any other component/member/logic to decide.
Edit:
As mentioned by Eran in the comments, the way Collections.sort works is using the compareTo method. From the docs:
orts the specified list into ascending order, according to the natural ordering of its elements. All elements in the list must implement the Comparable interface. Furthermore, all elements in the list must be mutually comparable (that is, e1.compareTo(e2) must not throw a ClassCastException for any elements e1 and e2 in the list).

For sorting collections a compareTo function is used to see if object1 is bigger or smaller than object2 by doing
object1.compareTo(object2)
According to documentation of Intger Object's compareTo
the value 0 if this Integer is equal to the argument Integer; a value
less than 0 if this Integer is numerically less than the argument
Integer; and a value greater than 0 if this Integer is numerically
greater than the argument Integer (signed comparison).
You can find a similar documentation for String Object. This same practice is used for all objects generally.
So, the idea is that the compareTo method in your class should basically return
<0 if object1 < object2
=0 if object1 = object2
>0 if object1 > object2
Using this the Collections API can sort objects.
So, in your case you can see
public int compareTo(Student compares) {
int compareage=((Student)compares).getAge();
/* For Ascending order*/
return this.age-compareage;
}
is used where it is allowing comparison of students based on their age.

Related

Comparator : Equals method functionality

Actually i am going through one of the tutorial in which it mentioned that when we need to implement the Comparator interface we can override equals method. however it is not necessary to override.
So just to understand better, I override the method as follows:
Test.java
import java.util.TreeSet;
public class Test
{
public static void main(String[] args)
{
TreeSet t = new TreeSet(new MyComparator());
t.add(1);
t.add(1);
t.add(2);
System.out.println(t);
}
}
MyComparator.java
import java.util.Comparator;
public class MyComparator
implements Comparator
{
#Override
public int compare(Object o1, Object o2)
{
Integer i1 = (Integer) o1;
Integer i2 = (Integer) o2;
return i1.compareTo(i2);
}
#Override
public boolean equals(Object o1)
{
return false;
}
}
For other scenario
import java.util.Comparator;
public class MyComparator
implements Comparator
{
#Override
public int compare(Object o1, Object o2)
{
Integer i1 = (Integer) o1;
Integer i2 = (Integer) o2;
return i1.compareTo(i2);
}
#Override
public boolean equals(Object o1)
{
return true;
}
}
No matter what I return from equals method either true or false .. it is returning the same TreeSet value.
Can anyone can clear up the concept of functionality of equals method please?
Implementing the equals() method on a Comparator allows you to indicate that one comparator provides the same ordering as another comparator. It has nothing to do with how your elements are sorted. It is a very advanced and extremely rarely needed functionality. You are highly unlikely to ever encounter a situation where your comparator's equals() method will actually be invoked. I would suggest that you ignore it.
Edit:
As an example of the very rare kind of circumstances under which Comparator equals() might be used:
If you look at the source code of TreeMap in function PutAll(Map) they have an optimization which checks whether the given map is also a TreeMap, and if so, then they check whether that map's comparator is equal to this map's comparator, and if so, they perform an optimized insertion of data which is known to already be sorted in the right order; otherwise, they delegate to super.PutAll(Map) which adds the items one by one.
equals() method of the class, Comparator and Comparator require equals consistency because some Java collection classes may behave unpredictably if the compareTo() and equals() methods are not returning consistent results.
Java uses == operator to compare two primitives and/or to check if two variables refer to the same object.
Example :
String apple1 = new String("apple");
String apple2 = new String("apple");
System.out.println(apple1.equals(apple2)); // true
StringBuilder app1 = new StringBuilder("apple");
StringBuilder app2 = new StringBuilder("apple");
System.out.println(app1.equals(app2)); // false
As you can see we get different behavior. Why is that? This happens because String class implements an equals() method, which checks that the values are the same. On the other hand, StringBuilder does not implement equals() method, instead it uses the implementation of equals() provided by Object class. And implementation provided(inherited) by Object class does only simply checks if the two referred objects are the same.
So to check if two objects are equivalent Java uses the equals() method and whenever you introduce your own type you must overrides equals() method if you do not want to rely on Object class implementation of equals() method
Let's for example introduce our own type : simple class Apple
public class Apple {
private int weight;
private int cost;
private String color;
Now how do would you decide if two apples are equal? By color, by weight, by price or something else? This is why you need to supply explicitly your own equals method, so objects of your type can be compared for equality.
Example below compares two Apple object for equality, and says that two object are equal if they belong to the same 'Apple' class and if their weight and cost is the same. Notice we do not compare by color, we assume that color is irrelevant in our case, which means that we accept the fact that apples of different colors, but with the same weight and same cost are considered equals.
#Override
public boolean equals(Object obj) {
if ( !(obj instanceof Apple)) return false;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Apple other = (Apple) obj;
if (cost != other.cost and weight != other.weight )
return false;
return true;
}
You can implement any logic you like for your equals() method. Java equals() method is very important and it provides contract or important rules developer shall follow. I will not list them, you get them from here, they are logic and pretty straight forward.
There is another contract of equals() - whenever you override equals(), you are also expected to override hashCode() method. The reason behind that is because hashcode is used internally by some of Java collections, when object is stored as a key in the map.
Hashcode is a number that categorizes object into categories. Imagine you are given various sorts of apples(red, green, yellow) and asked to give them back when asked for particular sort. It would be much efficient and faster if you categorize them, put each in a different buckets accordingly and whenever asked for particular sort (for simplicity let's say red apple), and as you have already sorted and categorized them, you can retrieve them much faster. Hope it is clear now why do you need to implement hashcode(). hashcode() has it's own contract or rules same as equals() method. They pretty much make common sense :
First, result of hashcode() within same program shall not change. What it means that in your hascode() calculations you should not include variable that my change during execution of a program. For example if cost of Apple's is mutable, i.e. it may change, it is not good idea to include them into hashcode() calculation, otherwise results would be inconsistent.
Second rule says, that when called with two objects, if equals() returns true then calling hashCode() on each of those objects must retrieve the same result. But if equals() returns false when called with two objects, calling hashCode() on each of those objects does not necessary have to return a different result. Confusing? Why? Because hashCode() results are not required to be unique when called on unequal objects - you may put two unequal object in one bucket.
Now regarding Comparator - it is easier to understand it's logic when you introduce your own type, instead of using built-in types.
For example let's say we have class Apple with two attributes : weight and price and want to put out object this type into sorted collection TreeSet.
public class Apple {
private int weight;
private int cost;
Now how do you want this apples to be sorted inside collection - do you want to sort them by weight or by price? How shall compiler decide?
Supplying appropriate Comparatoror Comparable allows you to pass your intent.
Let's try to add object to collection.
public class Apple {
private int weight;
private int cost;
public static void main(String[] args) {
Apple redApple = new Apple();
redApple.setCost(10);
redApple.setWeight(2);
Apple greenApple = new Apple();
greenApple.setCost(12);
greenApple.setWeight(3);
Set<Apple> apples = new TreeSet<>();
apples.add(redApple);
apples.add(greenApple);
System.out.println(apples);
}
public int getWeight() {
return weight;
}
public void setWeight(int weight) {
this.weight = weight;
}
public int getCost() {
return cost;
}
public void setCost(int cost) {
this.cost = cost;
}
#Override
public String toString() {
return "Apple [weight=" + weight + ", cost=" + cost + "]";
}
}
If you run above code you will get RuntimeError : Apple cannot be cast to java.lang.Comparable, because compiler has to way to figure out how do you want to compare your apples.
So let's fix it :
Let's implement Comparable interface
public class Apple implements Comparable<Apple> {
and override compareTo method
#Override
public int compareTo(Object obj) {
int cost = ((Apple) obj).getCost();
return this.getCost() - cost; // sorting in ascending order.
// change to this to sort in Descending order
// return cost - this.getCost();
}
Now with this changes, let's run our code :
[Apple [weight=2, cost=10], Apple [weight=3, cost=12]]
Our collection is sorted by cost in ascending order.
Now what if you don't have access to Apple class and you can't change source code to implement Comparable.
This is where Comparator helps.
Remove implements Comparable, as we assume we can't modify this class
public class Apple {
and remove method
#Override
public String toString() {
return "Apple [weight=" + weight + ", cost=" + cost + "]";
}
Add comparator implementation :
public class AppleComparator implements Comparator<Apple> {
#Override
public int compare(Apple app1, Apple app2) {
return app1.getCost() - app2.getCost();
}
}
Now I can just supply Comparator to collection to express my intend
Set<Apple> apples = new TreeSet<>(new AppleComparator());
Collection again will be sorted by cost, accordingly to supplied Comparator.
So we need to supply either Comparator or Comparable in order to get them stored in the collection, particularly in TreeSet.
Now regarding your question : There is no connection between Comparator and equals() method . There is only required consistency between Comparable (compareTo() method ) and equals() method.
Example - in above mentioned Apple class, in version that implements Comparable,
we introduce new logic for determining equality. The compareTo() method returns 0 if two objects are equal, while your equals() method returns true if two objects are equal.
A natural ordering that uses compareTo() required to be consistent with equals iff x.equals(y) is true whenever x.compareTo(y) equals 0. So you have to make your Comparable class consistent with equals because some Java collection classes may behave unpredictably if the compareTo() and equals() methods are not returning consistent results.
The following example shows compareTo() method that is not consistent with equals:
public class Apple implements Comparable<Apple> {
private int weight;
private int cost;
private String color;
public boolean equals(Object obj) {
if(!(obj instanceof Apple)) {
return false;
}
Apple other = (Apple) obj;
return this.weight == other.weight;
}
public int compareTo(Apple obj) {
return this.cost.compareTo(obj.cost); }
}
If we want to sort Apple objects by cost, but cost may not be unique. There could two objects with the same cost. Therefore, the return value of compareTo() might not be 0 when comparing two equal Apple objects, which mean that this compareTo() method is not consistent with equals().
The equals() method that you have override, is in the MyComparator class so it will be used if you need to compare 2 instance of Mycomparator ;)
And that's not what you're doing, you are comparing int so yes the compare() method is usefull because it's the one which will be used for sorting the Treeset but the equals method here won't be used
In 99% of the time, you don't need to override equals method in classes that implements Comparator, because they are here just to compare values not themself to another one, and in fact because most of the time they have to attributes, you won't have comparator equals.
I don't understand why you are linking the Comparator and the equals() method. The interfaces like Comparable and Comparatorare used for giving a comparison mechanism, like if you are putting data into a collection which is designed for sorting purpose, then it should have a comparison mechanism. Here is an example
package snippet;
import java.util.Iterator;
import java.util.Set;
import java.util.TreeSet;
class Person {
private int id;
private String name;
private String code;
private double salary;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
public boolean equals(Object obj) {
if(obj != null && (obj instanceof Person)) {
Person other = (Person) obj;
return this.code.equals(other.getCode());
}
return false;
}
public Person(int id, String name, String code, double salary) {
super();
this.id = id;
this.name = name;
this.code = code;
this.salary = salary;
}
}
public class EqualsMethodImpl
{
public static void main(String[] args) {
Set<Person> set = new TreeSet<Person>();
Person p1 = new Person(1, "Sam", "M-1-SAM-50", 50000.00);
Person p2 = new Person(2, "Diaz", "M-1-SAM-35", 35000.00);
Person p3 = new Person(3, "Remy", "M-1-SAM-100", 100000.00);
Person p4 = new Person(4, "Cesar", "M-1-SAM-80", 80000.00);
Person p5 = new Person(5, "Rino", "M-1-SAM-5", 5000.00);
set.add(p1);
set.add(p2);
set.add(p3);
set.add(p4);
set.add(p5);
printPersons(set);
}
private static void printPersons(Set<Person> set) {
System.out.println("Id\tName\tCode\t\tSalary");
Iterator<Person> perItr = set.iterator();
while(perItr.hasNext()) {
Person p = perItr.next();
System.out.println(p.getId()+"\t"+p.getName()+"\t"+p.getCode()+"\t"+p.getSalary());
}
}
}
While you running this code you will get ClasCastException at line set.add(p1);, why because the collection TreeSet is confused about on which attribute of Peron class should it do the sorting. That's where the interfaces like Comparable and Comparator have their significance.
Add this code to your Person class
public int compareTo(Person other) {
Double person1Salary = this.getSalary();
Double person2Salary = other.getSalary();
return person1Salary.compareTo(person2Salary);
}
by making your class Comparable type like Person implements Comparable<Person>
So by this code you are explaining the collection on which attribute we need to do the sorting. So when you print the content of Set it will be ordered on the base salary (ascending by default).
The Wrapper classes like Byte, Short, Integer, Long, Float, Double and other predefied classes like String all are implementing Comaparable interface and that's why you can simply pass them to a Sortable collection.
Now coming to equals method, it is used to check the equality of two objects, the traditional equals method inherited from Object class will check equality based on address locations. So take a case in Person class where I haven't overrided equals method and I'm writing a code like this
Person p1 = new Person(1, "Sam", "M-1-SAM-50", 50000.00);
Person p2 = new Person(1, "Sam", "M-1-SAM-50", 50000.00);
System.out.println(p1.equals(p2));
It will return false because both are two different objects placed in two different address locations, but we know that the objects are having data of the same person. That's the place where we override equals() method like I did in my Person class based on Person code. In that case System.out.println(p1.equals(p2)); will print true because the codes are equal. equals() method helps us in finding the duplicates. I hope this helps.
The equals method is used to check the Equality of two comparators i.e in equals method you can specify what makes ComparatorA and ComparatorB Identical, it has nothing to do with the elements that are sorted using these comparators.
you don't need to sort the values of Treeset. TreesSet elements are automatically sorted in ascending order.
class TreeSet1{
public static void main(String args[]){
//Creating and adding elements
TreeSet<String> al=new TreeSet<String>();
al.add("Ravi");
al.add("Vijay");
al.add("Ravi");
al.add("Ajay");
//Traversing elements
Iterator<String> itr=al.iterator();
while(itr.hasNext()){
System.out.println(itr.next());
}
}
}
output=
Ajay
Ravi
Vijay
First of all, please use generics. With the added compile-time type-safety, your code will be more robust, which in turn will make your life (and that of those who read your code) much easier (if you know what you're doing, of course). Your class MyComparator implements the raw type Comparator, which means that by just looking at its type declaration, you have no way of knowing which types of objects it compares. You have to look at the source (or, alternatively, at the documentation) in order to find out that, if any object that is not a subtype of Integer is passed, a ClassCastException will be thrown at runtime. On the other hand, if it were to implement Comparator<Integer>, the compiler will never let it come to this, because then, the method signature of the compare method would read compare(Integer, Integer) instead of compare(Object, Object), meaning that, if an object is not an Integer, it can never be passed to the method in the first place, which is something that can already be ensured at compile time.
Having that out of the way, you seem to be confusing Comparator and Comparable. A Comparable means that something has a "natural ordering", as Java calls it. An integer is a simple example, because a number is inherently quantifiable, so comparing two numbers is unambiguous (Double.NaN is a special case, but that is another matter). Thus, Integer implements Comparable<Integer> (but not Comparable<Number>, interestingly). However, occasions might arise when you want to compare things that are not inherently quantifiable. Arun Sudhakaran used the idea of comparing persons as an example in his answer. He suggested making the Person class implement Comparable<Person> so that you can pass it to a TreeSet<Person>, but this seems counterintuitive to me, for the precise reason that a person is not inherently quantifiable. You might want to sort the persons by their salary, but this quantification is not inherent to a person, it is only specific to your particular intention in that situation. So this would be a use case for a Comparator<Person>. You can sort the persons by their age, salary, height, whatever, by providing a respective Comparator<Person>, but a Person is not by itself comparable.
Note that the two are not mutually exclusive – you can also compare objects that implement Comparable by using a Comparator whose ordering differs from the natural ordering of the Comparable objects.
Now when the javadocs talk about comparisons being "consistent with equals", then "equals" in this context always refers to the equals(Object) method of the object to be compared, may that object implement Comparable itself or just be compared to another object with a Comparator. In other words, if a comparison method (be that a Comparator or a natural ordering) deems two objects equal that are also equal according to their respective equals(Object) method, and vice versa, this comparison method is "consistent with equals" This has nothing to do with the equals(Object) method of a Comparator, which was already pointed out by Mike Nakis (unless you want to compare Comparators).
An example of a natural ordering that is inconsistent with equals is that of the class BigDecimal. Two BigDecimals representing 2.0 and 2.00 will not be equal according to BigDecimal.equals(Object), but they will be considered equal by their natural ordering (i.e. by compareTo(BigDecimal)).

CompareTo Method and running time Assistance [duplicate]

I am learning about arrays, and basically I have an array that collects a last name, first name, and score.
I need to write a compareTo method that will compare the last name and then the first name so the list could be sorted alphabetically starting with the last names, and then if two people have the same last name then it will sort the first name.
I'm confused, because all of the information in my book is comparing numbers, not objects and Strings.
Here is what I have coded so far. I know it's wrong but it at least explains what I think I'm doing:
public int compare(Object obj) // creating a method to compare
{
Student s = (Student) obj; // creating a student object
// I guess here I'm telling it to compare the last names?
int studentCompare = this.lastName.compareTo(s.getLastName());
if (studentCompare != 0)
return studentCompare;
else
{
if (this.getLastName() < s.getLastName())
return - 1;
if (this.getLastName() > s.getLastName())
return 1;
}
return 0;
}
I know the < and > symbols are wrong, but like I said my book only shows you how to use the compareTo.
This is the right way to compare strings:
int studentCompare = this.lastName.compareTo(s.getLastName());
This won't even compile:
if (this.getLastName() < s.getLastName())
Use
if (this.getLastName().compareTo(s.getLastName()) < 0) instead.
So to compare fist/last name order you need:
int d = getFirstName().compareTo(s.getFirstName());
if (d == 0)
d = getLastName().compareTo(s.getLastName());
return d;
The compareTo method is described as follows:
Compares this object with the specified object for order. Returns a
negative integer, zero, or a positive integer as this object is less
than, equal to, or greater than the specified object.
Let's say we would like to compare Jedis by their age:
class Jedi implements Comparable<Jedi> {
private final String name;
private final int age;
//...
}
Then if our Jedi is older than the provided one, you must return a positive, if they are the same age, you return 0, and if our Jedi is younger you return a negative.
public int compareTo(Jedi jedi){
return this.age > jedi.age ? 1 : this.age < jedi.age ? -1 : 0;
}
By implementing the compareTo method (coming from the Comparable interface) your are defining what is called a natural order. All sorting methods in JDK will use this ordering by default.
There are ocassions in which you may want to base your comparision in other objects, and not on a primitive type. For instance, copare Jedis based on their names. In this case, if the objects being compared already implement Comparable then you can do the comparison using its compareTo method.
public int compareTo(Jedi jedi){
return this.name.compareTo(jedi.getName());
}
It would be simpler in this case.
Now, if you inted to use both name and age as the comparison criteria then you have to decide your oder of comparison, what has precedence. For instance, if two Jedis are named the same, then you can use their age to decide which goes first and which goes second.
public int compareTo(Jedi jedi){
int result = this.name.compareTo(jedi.getName());
if(result == 0){
result = this.age > jedi.age ? 1 : this.age < jedi.age ? -1 : 0;
}
return result;
}
If you had an array of Jedis
Jedi[] jediAcademy = {new Jedi("Obiwan",80), new Jedi("Anakin", 30), ..}
All you have to do is to ask to the class java.util.Arrays to use its sort method.
Arrays.sort(jediAcademy);
This Arrays.sort method will use your compareTo method to sort the objects one by one.
Listen to #milkplusvellocet, I'd recommend you to implement the Comparable interface to your class as well.
Just contributing to the answers of others:
String.compareTo() will tell you how different a string is from another.
e.g. System.out.println( "Test".compareTo("Tesu") ); will print -1
and System.out.println( "Test".compareTo("Tesa") ); will print 19
and nerdy and geeky one-line solution to this task would be:
return this.lastName.equals(s.getLastName()) ? this.lastName.compareTo(s.getLastName()) : this.firstName.compareTo(s.getFirstName());
Explanation:
this.lastName.equals(s.getLastName()) checks whether lastnames are the same or not
this.lastName.compareTo(s.getLastName()) if yes, then returns comparison of last name.
this.firstName.compareTo(s.getFirstName()) if not, returns the comparison of first name.
You're almost all the way there.
Your first few lines, comparing the last name, are right on track. The compareTo() method on string will return a negative number for a string in alphabetical order before, and a positive number for one in alphabetical order after.
Now, you just need to do the same thing for your first name and score.
In other words, if Last Name 1 == Last Name 2, go on a check your first name next. If the first name is the same, check your score next. (Think about nesting your if/then blocks.)
Consider using the Comparator interface described here which uses generics so you can avoid casting Object to Student.
As Eugene Retunsky said, your first part is the correct way to compare Strings. Also if the lastNames are equal I think you meant to compare firstNames, in which case just use compareTo in the same way.
if (s.compareTo(t) > 0) will compare string s to string t and return the int value you want.
public int Compare(Object obj) // creating a method to compare {
Student s = (Student) obj; //creating a student object
// compare last names
return this.lastName.compareTo(s.getLastName());
}
Now just test for a positive negative return from the method as you would have normally.
Cheers
A String is an object in Java.
you could compare like so,
if(this.lastName.compareTo(s.getLastName() == 0)//last names are the same
I wouldn't have an Object type parameter, no point in casting it to Student if we know it will always be type Student.
As for an explanation, "result == 0" will only occur when the last names are identical, at which point we compare the first names and return that value instead.
public int Compare(Object obj)
{
Student student = (Student) obj;
int result = this.getLastName().compareTo( student.getLastName() );
if ( result == 0 )
{
result = this.getFirstName().compareTo( student.getFirstName() );
}
return result;
}
If you using compare To method of the Comparable interface in any class.
This can be used to arrange the string in Lexicographically.
public class Student() implements Comparable<Student>{
public int compareTo(Object obj){
if(this==obj){
return 0;
}
if(obj!=null){
String objName = ((Student)obj).getName();
return this.name.comapreTo.(objName);
}
}

hascode and equals methods not overridden - How the put and get will work?

I have a class Student and Marks.
I am using Student Object as Key for HashMap and Marks as Value.
If I don't override hashMap and equals, It still works fine.
i. Can someone please explain how does it internally works on it if not overriding both equals() and hashcode()
ii. what If I override only hashcode()
iii.what If I override only equals()
class Student {
String name;
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
String lastName;
Student(String name, String lastName){
this.name = name;
this.lastName = lastName;
}
public String toString(){
return(" Name : " + this.getName() + " Last Name : " + this.getLastName());
}
}
class Marks {
Student s;
String marks;
public Student getS() {
return s;
}
public void setS(Student s) {
this.s = s;
}
public String getMarks() {
return marks;
}
public void setMarks(String marks) {
this.marks = marks;
}
Marks (Student s, String marks){
this.marks = marks;
this.s = s;
}
public String toString(){
return(" Marks : " + this.getMarks());
}
}
public class Main {
public static void main(String[] args) {
Student s1 = new Student("Vishnu","Verma");
Student s2 = new Student("Amit","Sharma");
Marks m1 = new Marks(s1,"65%");
Marks m2 = new Marks(s2,"67%");
Map <Student,Marks>map = new HashMap<Student,Marks>();
map.put(s1, m1);
map.put(s2, m2);
System.out.println(map);
}
}
It will consider the objects equal if their references are equal. i.e. they point to the same object.
Let us review how a HashMap works (in a simplistic case).
First we create our hash table:
we create a number of buckets - lets say 2
we then decide which hashcodes go in which buckets, lets say negative in one and positive (and zero) in the other.
When we add a key -> value mapping to our HashMap we:
calculate the hashCode of the key
given the hashCode and the above hash table we select a bucket
we determine if there is already something in that bucket, if there is we use a simple data structure (say a singly linked list) to store multiple key -> value mappings in the same bucket.
To get a value by key from our HashMap we:
calculate the hashCode of the key
given the hashCode and the above hash table we select a bucket
if there is a single item in the bucket we check whether they key of the mapping equals to the requested key, if so we return the value
if there is more than one item in the bucket we loop over the items in the bucket to find the first one where the key equals the requested key and return its value
if no such mapping exists we return null.
So, to answer your questions:
how does it internally works on it if not overriding both equals() and hashcode()?
I assume you mean if you do not override anything and leave it to the default behaviour.
In this case the HashMap uses Object.hashCode and Object.equals; the HashMap will behave exactly like an IdentityHashMap - i.e. it will use System.identityHashCode to calculate the hashCode and it will use == to test for equality.
This default behaviour will only work correctly when dealing with the same instance of a class.
what If I override only hashcode()?
TL;DR: Your HashMap will behave as above.
You hashCode will be used to select a bucket at insertion (2) and during retrieval (2) but the default equals will be used to determine whether the correct key is found.
It is likely that the Map will work similarly to the above case, as your hashCode implementation is unlikely to break the general contract of hashCode, i.e. only the exact same instance will be equal to itself and if hashCode compiles with the fact that it must return the same value when it is invoked multiple times on the same instance it must by extension comply with this requirement.
NB: The converse is not true. Overriding only equals immediately breaks the contract of hashCode due to items that are equals returning different hashcodes. This will break any hash based collection from HashMap to HashSet because you will have duplicate keys and the collection will exhibit Undefined Behaviour.
This issue will be that there are many more objects that you will return the same hashCode for than objects that you consider equals - whilst this is part of the very nature of hashing, you will compound this problem.
It is likely that your HashMap will be inefficient in this case.
To illustrate what I mean consider this simple example of a class with one element:
class IntHolder {
int value;
//getter setter
}
If we use the default equals and hashCode then the our Map would exhibit the following behaviour:
final Map<IntHolder, String> map = new HashMap<>();
final IntHolder a = new IntHolder();
a.setValue(7);
final IntHolder b = a;
final IntHolder c = new IntHolder();
c.setValue(7);
map.put(a, "A");
System.out.println(map.get(a)); // --> A
System.out.println(map.get(b)); // --> A
System.out.println(map.get(c)); // --> null
map.put(c, "C");
System.out.println(map.get(a)); // --> A
System.out.println(map.get(b)); // --> A
System.out.println(map.get(c)); // --> C
i.e. the Map will only consider two keys that are == (such as a and b) to be the same.
If you ever "lose" a reference to a key instance you will never be able to get it back. So if you did:
c = null;
You would never, without looping over the Map, be able to get "C" out of the Map again.
OP asks: And How we determine only hashCode() need to be overridden or only equals() need to be overridden?
You should only every override both or neither of these methods.
There is never a use case for overriding only equals or only hashCode.
If not implemented in your class, then hashCode and equals fallback to the Object implementations which are:
public native int hashCode();
public boolean equals(Object obj) {
return (this == obj);
}
So the implementation of hashCode varies depending on the runtime environment but more interestingly regarding HashMap, the equals methods require the two objects to be the same instance to be equal!
Object cannot guess for you what makes two instances equal so it defaults to the strongest constraint.
If you don't override equals and hashcode, the implementation is inherited from Object.
The method hashcode() is used by a HashMap to determine in a which sublist the entry will be in. So if you don't override (or even worse, override and give back the same value for each instance) the method, HashMap might store every entry in the same sublist. This will make the map slower, but putting in values will still work.
For your last question see https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#hashCode--
Long story short: You shouldn't override just one. You should override both.

Sorting array given two comparators?

Say I have two comparators, a primary and a secondary. How can I sort an array first by the primary comparator, then by the secondary?
Say each object has a name and a number field.
Like
Bob 1
Bob 2
Jack 1
Jack 2
Is it possible without creating a new comparator?
Yes, you can accomplish your sort without creating a new comparator.
There is a well-known trick for sorting by a primary field, secondary, tertiary, etc: First sort by the least important field (tertiary), then the next important field (secondary), and finally the most important field (primary). But the sorting algorithm needs to be stable for this to work.
If you are sorting an array, use Arrays.sort(). If you are sorting a List, use Collections.sort(). Both of these methods are guaranteed to be stable.
Suppose your primary comparator object is stored in the variable primaryComp, and your secondary is in secondaryComp. Then here is some code to accomplish what you want:
Arrays.sort(mylist, secondaryComp); // This must come first!
Arrays.sort(mylist, primaryComp);
assuming your class is
class X {
String name;
int num;
}
then sorting will be
Arrays.sort(x, new Comparator<X>() {
#Override
public int compare(X o1, X o2) {
if (o1.name.equals(o2.name)) {
return Integer.compare(o1.num, o2.num);
}
return o1.name.compareTo(o2.name);
}});
Compare the second comparator first, and then the first comparator. I believe that should do the trick. You can create a class to do so.
class FullName {
public String firstName;
public String secondName;
}
Say you create a new name, called BobBobbins, assign values and then simple compare the second name first, and then the first name. You can have a static function to do the comparing:
public static bool compareTo ( FullName name1, FullName name2 ) {
// Algorithm here
}
Should you use a static comparator, you can will have to do this : FullName.compareTo( BobBobbins, CharlieChaplin );

Counting sort in java for tuples

I am building a class that has a mapping of strings to integers. So if I have 3 apples I would have a mapping of apples to 3.
I need to write a class that sorts the name of the objects by decreasing numbers.
So if I have
(apples, 3)
(oranges, 2)
(bananas, 5)
I will get
(bananas, 5), (apples, 3), (oranges 2)
I was wondering if there's already a class out there that would make my life easier or how I would implement this.
Thanks.
You should be able to put your objects (apples, 3) (oranges, 2) (bananas, 5) into a List and then call Collections.sort(yourlist). You'd then want to make sure the object you declared implements the Comparable interface.
More information is available at http://java.sun.com/docs/books/tutorial/collections/interfaces/order.html
Let's say you declared you object as
public class FruitAndCount implements Comparable<FruitAndCount> {
private final String name;
private final Integer count;
public FruitAndCount(String name, int count) {
this.name = name;
this.count = count;
}
public String name() { return name; }
public int count() { return count; }
public int compareTo(FruitAndCount o) {
return this.count.compareTo(o.count);
}
}
You should then be able to make the following call which will sort your list:
FruitAndCount fruitArray[] = {
new FruitAndCount("Apples", 3),
new FruitAndCount("Oranges", 2),
new FruitAndCount("Bananas", 5)
};
List<FruitAndCount> fruit = Arrays.asList(fruitArray);
Collections.sort(fruit);
You should then have a sorted list of fruit.
It's always nice to be able to make a class implement Comparable, but sometimes you can't, or it is undesirable (for instance, if you need to be able to compare the same type in different ways, based on different attributes).
In this case, it is advisable to use the overloaded Collections.sort() method, which takes a List<T> to sort and a Comparator<T> to determine how the objects should be sorted. This is much cleaner than making new tuples out of your old tuples, and can be more flexible than implementing Comparable (which is also a valid solution).
You really want to take a look at TreeMap.
Assuming the counts are unique, you simply reverse the tuples, storing the count as the key and the name of the fruit as the value. TreeMap then stores the items sorted in ascending order by the key value, and you can read the values back. Since the sorting is done on the insertion the retrieval time is very low.
If you have non-unique counts there's an easy solution here that will let you take advantage of TreeMap.

Categories

Resources