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
Related
I have the following class Person -
Person.java -
public class Person {
private int id;
private String department;
private double salary;
public Person(int id, String department, double salary) {
this.id = id;
this.department = department;
this.salary = salary;
}
public String getDepartment() {
return department;
}
public double getSalary() {
return salary;
}
#Override
public String toString() {
return "Person{" +
"id=" + id +
", department='" + department + '\'' +
", salary=" + salary +
'}';
}
}
It has the fields -
id, department, salary
Now I have first predicate -
Predicate<List<Person>> hasSalaryOf40k = list -> {
boolean myReturn = false;
Iterator<Person> iterator = list.iterator();
while (iterator.hasNext()) {
Person person = iterator.next();
double salary = person.getSalary();
if (salary == 40000) {
myReturn = true;
break;
}
}
return myReturn;
};
Here, I want to filter out those lists having persons with salary as 40K.
Second predicate -
Predicate<List<Person>> isDeveloper = list -> {
boolean myReturn = false;
Iterator<Person> iterator = list.iterator();
while (iterator.hasNext()) {
Person person = iterator.next();
String department = person.getDepartment();
if (department.equals("Developer")) {
myReturn = true;
break;
}
}
return myReturn;
};
Here, I want to filter out those lists having persons with department as 'developer'
Third predicate -
Predicate<List<Person>> hasSalaryOf40kAndIsDeveloper = list ->
hasSalaryOf40k.and(isDeveloper).test(list);
Here, I want to filter out those lists having persons with both salary as 40K and department as "developer"
Now I have the following two lists -
List<Person> list1 = new ArrayList<>(List.of(
new Person(1, "Developer", 35000),
new Person(2, "Accountant", 40000),
new Person(3, "Clerk", 20000),
new Person(4, "Manager", 50000)
));
List<Person> list2 = new ArrayList<>(List.of(
new Person(1, "Developer", 40000),
new Person(2, "Accountant", 35000),
new Person(3, "Clerk", 22000),
new Person(4, "Manager", 55000)
));
The list1 does not match the desired criteria while list2 matches the desired criteria.
Now I call the predicate method test -
System.out.println(hasSalaryOf40kAndIsDeveloper.test(list1));
System.out.println(hasSalaryOf40kAndIsDeveloper.test(list2));
Output -
true
true
Desired output -
false
true
Where am I going wrong and how to correct my code?
You're applying the predicate to the whole list and not each element of the list, so it's true that the list contains a developer and its true that the list contains a salary over 40k. You need to apply the predicate to the Person object rather than the List<Person> object
I know that both hashcode and equals need to be overriden. I tried below program and was wondering why list is able to search student when set couldn't search student when only equals is overridden:
import java.util.*;
class Student {
private int id;
private String name;
public Student(int id, String name) {
this.name = name;
this.id = id;
}
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;
}
#Override
public boolean equals(Object obj) {
if (obj == null) return false;
if (!(obj instanceof Student))
return false;
if (obj == this)
return true;
return this.getId() == ((Student) obj).getId();
}
}
public class StudentHashcodeEquals {
public static void main(String[] args) {
Student alex1 = new Student(1, "Alex");
Student alex2 = new Student(1, "Alex");
System.out.println("alex1 hashcode = " + alex1.hashCode());
System.out.println("alex2 hashcode = " + alex2.hashCode());
System.out.println("Checking equality between alex1 and alex2 = " + alex1.equals(alex2));
List<Student> studentsLst = new ArrayList <Student>();
studentsLst.add(alex1);
studentsLst.add(alex2);
System.out.println("Arraylist size = " + studentsLst.size());
System.out.println("Arraylist contains Alex = " + studentsLst.contains(new Student(1, "Alex")));
HashSet <Student> students = new HashSet <Student>();
students.add(alex1);
students.add(alex2);
System.out.println("HashSet size = " + students.size());
System.out.println("HashSet contains Alex = " + students.contains(new Student(1, "Alex")));
}
}
/*
alex1 hashcode = 366712642
alex2 hashcode = 1829164700
Checking equality between alex1 and alex2 = true
Arraylist size = 2
Arraylist contains Alex = true
HashSet size = 2
HashSet contains Alex = false
*/
When student was inserted into list, I'm assuming that it would insert it into 2 different buckets as the hashcode implementation is not prsent and default object hashcode would get kicked in and it'll store them into 2 different hash codes and when searching for another object, it has to first locate the bucket via hash code but hash code implementation is not there, then how list is able to still identify it whereas set does not??
Thanks!
Lists do not use the hashcode of an object. Depending on their type, they store an array of references to the objects.
If you call contains on a list, it will iterate through the list using .equals() to check if the object is in the list.
This question already has answers here:
Sort ArrayList of custom Objects by property
(29 answers)
Closed 6 years ago.
My code is as follows:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class Sorting {
public static void main(String[] args) {
// TODO Auto-generated method stub
List<String> theMenu = new ArrayList<String>();
String[] Array1 = {
"M1", "Wine", "2.50",
"M2", "Soft drink", "1.50",
"D1", "Fish", "7.95",
"D2", "Veg chili", "6.70"
};
theMenu.addAll(Arrays.asList(Array1));
String[] temp1 = new String[3];
String[] temp2 = new String[3];
for (int i = 0; i < theMenu.size(); i+=3) {
for (int j = i + 3; j < theMenu.size(); j+=3) {
if (i < theMenu.size() - 3) {
if (theMenu.get(i).compareTo(theMenu.get(i + 3)) > 0) {
temp1[0] = theMenu.get(i);
temp1[1] = theMenu.get(i + 1);
temp1[2] = theMenu.get(i + 2);
temp2[0] = theMenu.get(j);
temp2[1] = theMenu.get(j+1);
temp2[2] = theMenu.get(j+2);
theMenu.remove(j + 2);
theMenu.remove(j + 1);
theMenu.remove(j);
theMenu.remove(i + 2);
theMenu.remove(i + 1);
theMenu.remove(i);
theMenu.add(i, temp2[0]);
theMenu.add(i + 1, temp2[1]);
theMenu.add(i + 2, temp2[2]);
theMenu.add(j, temp1[0]);
theMenu.add(j + 1, temp1[1]);
theMenu.add(j + 2, temp1[2]);
}
}
}
}
System.out.println(theMenu);
}
}
I want to sort the ArrayList in the order D1, D2, M1, M2, M3, while keeping its respective items and price WITH the IDs. I am not allowed to change the storing method i.e make another class of Items with its own ID and name and price. How can I rearrange it so that it is in the form :
{"D1" , "Fish", "7.95"
"D2" , "Veg chili", "6.70",
"M1" , "Wine", "2.50",
"M2", "Soft drink", "1.50"
}
Inside the ArrayList. This should work regardless of how many items we store inn the arrayList. My code produces the following output:
[M1, Wine, 2.50, M2, Soft drink, 1.50, D1, Fish, 7.95, D2, Veg chili, 6.70]
Note: Forget the new lines in the array, I just need the indexes sorted out. Can anyone help me with this?
Firstly, you have a entity - product, which has Id, name and price.
Always create new class for each Entity in your application.
Example:
public class MyObject implements Comparable
{
private String id;
private String name;
private double price;
public MyObject(String id, String name, double price)
{
this.id = id;
this.name = name;
this.price = price;
}
public String getId()
{
return id;
}
#Override
public int compareTo(Object o)
{
MyObject receivedObject = (MyObject) o;
return this.id.compareTo(receivedObject.getId());
}
#Override
public String toString()
{
return "MyObject{" +
"id='" + id + '\'' +
", name='" + name + '\'' +
", price=" + price +
'}';
}
}
I use "implements Comparable" for easy write sorting. When we implement this interphace we must override
public int compareTo(Object o)
{
MyObject receivedObject = (MyObject) o;
return this.id.compareTo(receivedObject.getId());
}
This method compare 2 Objects and say which object "bigger". In your case we need compare only by Ids.
Now you have entity with ability to compare with each other. Check it:
public class Processing
{
public static void main(String[] argc) {
List<MyObject> list = new ArrayList<>();
list.add(new MyObject("M1", "Wine", 2.50));
list.add(new MyObject("M2", "Soft drink", 1.50));
list.add(new MyObject("D1", "Fish", 7.95));
list.add(new MyObject("D2", "Veg chili", 6.70));
System.out.println(list);
Collections.sort(list);
System.out.println(list);
}
}
First output:
MyObject{id='M1', name='Wine', price=2.5},
MyObject{id='M2', name='Soft drink', price=1.5},
MyObject{id='D1', name='Fish', price=7.95},
MyObject{id='D2', name='Veg chili', price=6.7}
Second output:
MyObject{id='D1', name='Fish', price=7.95},
MyObject{id='D2', name='Veg chili', price=6.7},
MyObject{id='M1', name='Wine', price=2.5},
MyObject{id='M2', name='Soft drink', price=1.5}
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 7 years ago.
Improve this question
i have a question on how to remove duplicate for multiple Array. Basically what i want to achieve is to compare from 4 Array and if the contain of Array are exactly the same then i want do not want to print out.
The example of contains for each as below shown:
ArrayStudentName ArrayAge ArrayGender ArrayCourse
A 9 Boy IT
B 10 Boy IT
B 9 Boy IT
A 9 Boy IT
A 9 Girl IT
Is that the only way to remove the duplicate with using 4 times of for loops or do i have to merge 4 arrays into 1 array and then use HashMap to remove duplicate before u print out the result.
The result i need is:
ArrayStudentName ArrayAge ArrayGender ArrayCourse
A 9 Boy IT
B 10 Boy IT
B 9 Boy IT
A 9 Girl IT
You should be brave and take the OOP route. Make a class called Student and fold all the values into that class. Then you just need to put them in a Set which, by definition, does not allow duplicates.
You do, however, have to implement equals and hashcode (or Comparable if you choose not to use a HashSet).
enum Gender {
Boy, Girl, Other;
}
enum Course {
IT, CS;
}
class Student {
final String name;
final int age;
final Gender gender;
Course course;
public Student(String name, int age, Gender gender) {
this.name = name;
this.age = age;
this.gender = gender;
}
public Course getCourse() {
return course;
}
public void setCourse(Course course) {
this.course = course;
}
#Override
public String toString() {
return name + "\t" + age + "\t" + gender + "\t" + course + "\n";
}
#Override
public int hashCode() {
int hash = 5;
return hash;
}
#Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final Student other = (Student) obj;
if (!Objects.equals(this.name, other.name)) {
return false;
}
if (this.age != other.age) {
return false;
}
if (this.gender != other.gender) {
return false;
}
return true;
}
}
public void test() {
System.out.println("Hello");
List<Student> students = Arrays.asList(
new Student("A", 9, Gender.Boy),
new Student("B", 10, Gender.Boy),
new Student("B", 9, Gender.Boy),
new Student("A", 9, Gender.Boy),
new Student("A", 9, Gender.Girl)
);
// Fold it into a Set to eliminate duplicates.
Set all = new HashSet();
all.addAll(students);
// Pull back out into a List.
System.out.println("Students:\n" + all);
}
Create a Student Class.
public class Student {
private String name;
private int age;
private String gender;
private String course;
// Implement Getters and Setters here.
// Then Override the equals method like this:
#Override
public boolean equals(Object obj) {
Student student = (Student) obj;
if ( this.name.equals( student.getName() ) &&
this.age == student.getAge() &&
this.gender.equals( student.getGender() ) &&
this.course.equals( student.getCourse() )
) {
return true;
}
else {
return false;
}
}
}
Now you can create an array of Student objects and compare them using equlas method.
easy way: use a Set (to get unique entries), an keep order: LinkedHashSet
Then Read each line in a List< String >
and keep them in a LinkedHashSet< List< String> >
This gives something like that (you must loop and scan your datas of course !)
Set<List<String>> sls=new LinkedHashSet<List<String>>();
sls.add(Arrays.asList(new String[]{"A","9","Boy","IT"}));
sls.add(Arrays.asList(new String[]{"B","10","Boy","IT"}));
sls.add(Arrays.asList(new String[]{"B","9","Boy","IT"}));
sls.add(Arrays.asList(new String[]{"A","9","Boy","IT"}));
sls.add(Arrays.asList(new String[]{"A","9","Girl","IT"}));
System.out.println(sls);
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);