Working of == in the Comparable interface used in ArrayList - java

Working of == operator in Comparable Interface
Employee.java
class Employee implements Comparable
{
int id; String name; int age;
Employee(int id,String name,int age)
{
this.id=id;
this.name=name;
this.age=age;
}
public int compareTo(Object obj)
{
Employee emp = (Employee)obj;
if(age==emp.age)
{
return 0;
}
//else if(age>emp.age)
//return 1;
else
return -1;
}
}
display_logic.java
import java.util.*;
class display_logic
{
public static void main(String args[])
{
ArrayList al = new ArrayList();
al.add(new Employee(1,"Supreeth",21));
al.add(new Employee(2,"Vijay",31));
al.add(new Employee(3,"Ganesh",21));
al.add(new Employee(4,"Aisu",31));
al.add(new Employee(5,"Aizzz",41));
Collections.sort(al);
Iterator it = al.iterator();
while(it.hasNext())
{
Employee emp = (Employee)it.next();
System.out.println("Employee name" +emp.name+ "," +emp.age);
}
}
}
Please let me how does == operator work
Im not able to get the logic that is being implemented in the output
Output
Employee name Aizzz,41
Employee name Aisu,31
Employee name Ganesh,21
Employee name Vijay,31
Employee name Supreeth,21
Thank You In Advance

The contract of compareTo says:
The implementor must ensure sgn(x.compareTo(y)) == -sgn(y.compareTo(x)) for all x and y.
So, when using it like this:
Employee emp = (Employee)obj;
if(age==emp.age)
return 0;
else
return -1;
it doesn't work at all, because you may have both
emp1.compareTo(emp2) == -1 // "emp1 should come before emp2"
and
emp2.compareTo(emp1) == -1 // "emp2 should come before emp1"
which is a violation of the contract. This means that "all bets are off" and any method taking advantage of compareTo (such as Collections.sort) has undefined behavior.
You can use == but you'll have to take better care of the != case:
Employee emp = (Employee)obj;
if(age==emp.age)
return 0;
else if (age < emp.age)
return -1;
else
return 1;
A better way however is to do
return Integer.compare(age, emp.age);

Related

TreeSet is adding duplicate values to the Set

I am solving a problem. I have to create a TreeSet of custom Employee objects where data should be sorted by salary but employee Id needs to be unique. I understand that equals() and hashCode() method doesn't work for TreeSet and we need to write our object are equal or not logic inside the compareTo() method. I am checking if both employee Id are equal then return 0, means object should not be added.
But the output is not coming as desired as employees with same employee Id are also getting added. I tried to debug this but I am not getting the right answer.
This is the code.
public class Employee implements Comparable<Employee>{
int empId;
String empName;
double salary;
public Employee() {
super();
}
public Employee(int empId, String empName, double salary) {
super();
this.empId = empId;
this.empName = empName;
this.salary = salary;
}
#Override
public int hashCode() {
return empId;
}
#Override
public boolean equals(Object o) {
if(this == o) return true;
if(o == null || this.getClass() != o.getClass()) return false;
Employee e = (Employee) o;
return (this.empId == e.empId);
}
#Override
public String toString() {
return empId + " " + empName + " " + salary;
}
#Override
public int compareTo(Employee e) {
if(empId == e.empId)
return 0;
if(this.salary < e.salary) {
return -1;
}
else {
return 1;
}
}
}
The main method of program
public static void main(String[] args) {
TreeSet<Employee> eSet = new TreeSet<>();
eSet.add(new Employee(1, "john", 20000));
eSet.add(new Employee(2, "jim", 10000));
eSet.add(new Employee(9, "mike", 50000));
eSet.add(new Employee(3, "jack", 30000));
eSet.add(new Employee(3, "david", 40000));
eSet.add(new Employee(9, "liam", 80000));
eSet.add(new Employee(9, "brad", 89000));
eSet.add(new Employee(3, "jason", 85000));
eSet.add(new Employee(2, "ted", 35000));
for(Employee e: eSet) {
System.out.println(e);
}
}
The output of above program comes out as
2 jim 10000.0
1 john 20000.0
3 jack 30000.0
2 ted 35000.0
9 mike 50000.0
3 jason 85000.0
Here as you can see employees with same employee Id are being added to the TreeSet which should not happen. If I am using a HashSet the problem is resolved, but I have to implement it using TreeSet to get the sorted behaviour.
Can someone please guide me where am I going wrong?
The implementation of Comparable violates the contract of Comparable::compareTo, in particular this part:
Finally, the implementor must ensure that x.compareTo(y)==0 implies that signum(x.compareTo(z)) == signum(y.compareTo(z)), for all z.
We can demonstrate this violation with the following code:
final Employee jim = new Employee(2, "jim", 10_000);
final Employee ted = new Employee(2, "ted", 35_000);
final Employee john = new Employee(9, "john", 20_000);
System.out.println("jim compare to ted: " + jim.compareTo(ted));
System.out.println("john compare to jim: " + john.compareTo(jim));
System.out.println("john compare to ted: " + john.compareTo(ted));
leading to the following output:
jim compare to ted: 0
john compare to jim: 1
john compare to ted: -1
Ideone demo
We can fix this issue by dropping the salary from the compareTo-method and only order by empId:
#Override
public int compareTo(Employee e) {
return Integer.compare(empId, e.empId);
}
Ideone demo

How can I override the compareto method to order Employees's salary?

I have 2 classes: person and employee and an interface Human
Within my Person class, I have a compareTo Method (Human h)
Which assigns the +1,-1 and 0 for the person's age. My class employee =
public class Employee extends Person implements Human =
I have a compareTo method as well, which needs to account for the employees salary if the age is the same (for sorting).
I am not quite sure how to tackle this?
I was able to make the compreTo for the Persons class but I am not sure how to have both person and employe sorted here.
Thank you for the help.
I have already tried this in my Employee class:
compareTo (Human h) {
Employee e = (Employee)h;
if (super.compareTo(h) == 0 && getSalary ()< e.getSalary())
return -1;
else if (super.compareTo(h) == 0 && getSalary () == e.getSalary())
return 0;
else
return 1;
}
This one works, but I want to be able to use instanceof to solve this problem:
public int compareTo(Human h) {
// TODO Auto-generated method stub
if (getAge() < h.getAge()) {
return -1;
} else if (getAge() > h.getAge()) {
return 1;
} else {
Employee e = (Employee)h;
// age is identical: compare salary
if (getSalary() < e.getSalary()) {
return -1;
} else if (getSalary() > e.getSalary()) {
return 1;
} else {
return 0;
}
}
}
Below I had proved the amount of code I think is necessary for this question:
public interface Human extends Comparable <Human>{
//extends = is a
int getAge();
String getName();
}
public class Person implements Human {
private int age;
private String name;
public int compareTo(Human h) {
//System.out.println(this.age + ". " +h.getAge());
if (h.getAge() > getAge())
return -1;
else if (getAge() == h.getAge())
return 0;
else
return 1;
}
public class Employee extends Person implements Human{
private int salary;
private String employer;
public int compareTo(Human h) {
???
}
public static void main(String[] args) {
ArrayList<Human> p = new ArrayList<Human>();
p.add(new Person("A", 1));
p.add(new Employee("B", 31, "E1", 45000));
p.add(new Person("C", 122));
p.add(new Employee("D", 3, "E2", 54321));
p.add(new Person("E", 21));
p.add(new Employee("F", 31, "E1", 21000));
p.add(new Employee("G", 31, "E1", 38000));
System.out.println(p);
Collections.sort(p);
System.out.println(p); }
This is what I am trying to test:
non sorted: [Person:[A, 1], Employee:[B, 31][E1, 45000], Person:[C, 122], Employee:[D, 3][E2, 54321], Person:[E, 21], Employee:[F, 31][E1, 21000], Employee:[G, 31][E1, 38000]]
sorted: [Person:[A, 1], Employee:[D, 3][E2, 54321], Person:[E, 21], Employee:[F, 31][E1, 21000], Employee:[G, 31][E1, 38000], Employee:[B, 31][E1, 45000], Person:[C, 122]]
Any help would be appreciated.
To ensure a correct ordering; the compareTo method needs to satisfy the contract specified by the Comparable interface.
Unfortunately, there is no way to extend Person; overriding compareTo in Employee to compare the salary while preserving the contract.
A simple solution is to pass a comparator to Collections.sort(); ensuring that all the elements of the collection use the same comparator implementation:
Comparator.comparingInt(Human::getAge).thenComparingInt(h -> h instanceof Employee ? ((Employee) h).getSalary() : 0)
You can achieve this by simply implementing the compareTo method in Person and Employee as follow:
// In Person class
#Override
public int compareTo(Human h) {
return age - h.getAge();
}
And
// In Employee class:
#Override
public int compareTo(Human h) {
int result = super.compareTo(h);
if ((result == 0) && (h instanceof Employee)) {
result = salary - ((Employee) h).salary;
}
return result;
}
Cheers!

searching custom objects in arrays and arraylists in java

I have written an employee class which has a display method and the constructor.
public class employee {
public int empid;
public String name;
public employee(int id, String name){
empid = id;
this.name = name;
}
public void display (){
System.out.println("Employee id: " +empid +"\nEmployee name: "+name);
}
}
I then created three objects of employee and stored them in an array in the main class. I created an if block which will check user guess and print the employee details if he exists or will throw an exception if the data is not present. The if block was enclosed in an enhanced for loop which loops through the array.
public static void main(String[] args) {
// TODO code application logic here
employee priya = new employee (001, "Priya");
employee tamizh = new employee (002, "Tamizh");
employee hari = new employee (003, "hari");
employee[] list = new employee[3];
list[0] = priya;
list[1] = tamizh;
list[2] = hari;
int userGuess = 002;
for (employee l : list){
if (userGuess == l.empid)
{
l.display();
break;
}
else
{
throw new InputMismatchException ("employee doesnot exist");
}
}
}
}
The trouble is that the program throws the exception even if the guess is correct. I tried the int variable empid and then the String variable name, but both the == and .equals didn't work. I searched stackoverflow and the solution suggested was to override the hashcode and equals method in the employee class. I did that.
public class employee {
public int empid;
public String name;
public employee(int id, String name){
empid = id;
this.name = name;
}
public void display (){
System.out.println("Employee id: " +empid +"\nEmployee name: "+name);
}
#Override
public int hashCode(){
final int prime = 31;
int result = 1;
result = prime * result + empid;
result = prime * result + name.hashCode();
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
employee other = (employee) obj;
if (this.empid != other.empid)
return false;
if (this.name.equals(other.name))
return false;
return true;
}
}
Now when I give the input as 001, the code works fine. But for any other input (including the existing empids 002 and 003), the exception is being thrown.
What had I done wrong in the overriding? Also I don't understand the code I had written to override the two methods. Can someone explain the logic and where I went wrong? Thanks.
Edit: Thanks guys. I have realized my mistake in the for loop and I have edited it. It works perfectly now.
int userGuess = 002;
boolean found = false;
for (employee l : list){
if (userGuess == l.empid)
{
l.display();
found = true;
break;
}
}
if(found == false){
try{
throw new InputMismatchException ("employee doesnot exist");
}
catch(InputMismatchException e){
System.out.println("Employee doesnot exist.");
}
}
Thanks a lot guys. Can someone explain what I have done in equals and hashcode methods? I copied the code from an answer and I couldn't find an explanation for it. Thanks again.
You iterate through your whole array starting at the first entry.
So if you compare the first entry with your user input (lets say its 002) the statement will be false. So it will throw an exception.
To solve this issue you would have to check if an entry has been found AFTER iterating through your array.
int userGuess = 002;
boolean userFound = false;
for (employee l : list)
{
if (userGuess == l.empid)
{
userFound = true;
l.display();
break;
}
}
if(!userFound)
{
throw new InputMismatchException ("employee doesnot exist");
}
To answer your second question:
i dont think you'll need the equals() and hashCode() method.
The equals checks if two objects are the same (see https://msdn.microsoft.com/de-de/library/bsc2ak47(v=vs.110).aspx)
the hashCode() method generates a "unique" value for an object (see https://msdn.microsoft.com/de-de/library/system.object.gethashcode(v=vs.110).aspx)
This should work for you:
boolean found = false;
for (employee l : list){
if (userGuess == l.empid) {
l.display();
found = true;
break;
}
}
if(!found){
throw new InputMismatchException ("employee doesnot exist");
}
Your existing code will not work because the first in the loop is always 001.

How do I get parameter from other parameters of same constructors (java)

So I was supposed to identify a parameter based on other parameters of same constructor, for example:
public Student(String n, int sN, int hN){
this.name = n;
this.streetNum = sN;
this.houseNum = hN;
}
The main class will be provided with all the parameters filled like this,
Student a = new Student("Abigail", 1, 5);
so I need to figure out how to find the the name "Abigail" from streetNum 1 and houseNum 5 of students as given.
You could provide a utility class that does that with a method that takes the attributes and the array of students. Additional checks may be required.
public class Students {
public static String findStudentName(int sN, int hN, Student[] students) {
for (Student s : students) {
if (s.getSN() == sN && s.getHN() == hN) {
return s.getName();
}
}
return null;
}
}
The above code will return the first student's name with matching attributes or null otherwise.
You can include below method in your Student Class
public static Student getStudent(Student[] students, String sName, int streetNum, int houseNum) {
for (Student student : students) {
if (sName.equals(student.getName()) && student.getStreetNum() == streetNum
&& student.getHouseNum() == houseNum) {
return student;
}
}
return null;
}
and acccess it like below
Student[] students = ...// student array
Student s = Student.getStudent(students, "Abigail", 1, 5);
System.out.println(s != null ? s.getName() : "No match found");

Compare arraylist objects in java

I have list of objects in an arraylist and I need to compare every objects with other objects available in the arraylist;
For Example:
Class Employee {
private String empname;
private Long empid;
private boolean empsex;
public String getEmpname() {
return empname;
}
public void setEmpname(String empname) {
this.empname = empname;
}
public Long getEmpid() {
return empid;
}
public void setEmpid(Long empid) {
this.empid = empid;
}
public boolean isEmpsex() {
return empsex;
}
public void setEmpsex(boolean empsex) {
this.empsex = empsex;
}
}
public list<Employee> getEmpList() {
List<Employee> empList = new ArrayList<Employee>();
Employee emp = new Employee();
for(...) {
//insert values to emp object for n number of times;
}
empList.add(emp); //add emp.object to empList;
return empList;
}
Now while inserting these values to UI; Need to compare objects in the list; where any two or more objects matches with each other or not?
Based on the assumption that you want to eliminate the duplicates from the list and do not show duplicates on GUI.
Use Set collection, it automatically takes care of duplicates.
A collection that contains no duplicate elements. More formally, sets
contain no pair of elements e1 and e2 such that e1.equals(e2), and at
most one null element. As implied by its name, this interface models
the mathematical set abstraction.
Override equals() and hashcode() methods.
References:
On equals and hashcode in Java
Overriding equals and hashcode
You could override equals method for that class to compare the objects of the same class the way you want.
Default equals method:
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String) anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
Link about overriding equals:
http://javarevisited.blogspot.com/2011/02/how-to-write-equals-method-in-java.html
One simple method of doing this is to just do a double for loop.
public static List<Employee> getList(List<Employee> oldList)
{
List<Employee> empList = new ArrayList<Employee>();
for (int i = 0; i < oldList.size; i++)
{
for (int j = 0; j < oldList.size; j++)
{
//compare oldList.get(i) with oldList.get(j)
//if match, set some boolean
}
//if duplicate found, delete one copy, or add one to new list, etc
}
This allows you to go through each element in the outer loop, and compare to every other element in the inner loop.
I guess employee id (empid) is unique for each emplyee, yes?
If so, use a hash instead where empid is the key and employee object is the value.
Map<Long, Employee> empmap = new HashMap<Long, Employee>();
empmap.put(currentEmployee.getEmpid(), currentEmployee)

Categories