public class Drink implements Comparable {
public String name;
#Override
public int compareTo(Object o) {
return 0;
}
#Override
public String toString() {
return name;
}
public static void main(String[] args) {
Drink one = new Drink();
Drink two = new Drink();
one.name = "Coffee";
two.name = "Tea";
TreeSet set = new TreeSet();
set.add(one);
set.add(two);
Iterator itr = set.iterator();
while(itr.hasNext()) {
System.out.println(itr.next()); //prints Tea
}
}
}
Usually, compareTo() method prints in lexicographical order, but when compareTo() method is overridden as in the above code then how it is comparing the two strings?
According to your compareTo method, all objects are equal to each other, since you always return 0, so when you try to add two Drink objects to your TreeSet, only the first one will be added, since a Set doesn't allow duplicates.
It would make more sense to have an implementation like this, that actually compares the names :
public class Drink implements Comparable<Drink> {
public String name;
#Override
public int compareTo(Drink o) {
return name.compareTo(o.name);
}
...
}
It is not comparing the string in this as thecomapareTo() method is returning 0 (meaning objects are equal) so set.add(two) will be considered as duplicated and only the first value added will be printed.
Try reversing the order of addition of values to the set and you will get your answer
The overridden compareTo method is used for customize comparison. In this function, you compare two objects based on your business logic and on the basis of your logic, you return -1,0 or 1, where -1 represents invoking object is smaller than the invoked object while _1 represents the other way. 0 represents that both the objects are equal.
In your code, right now it is not putting any logic. Its just returning a prototype value. You might put something like that in your code
return name.compareTo((String)o);
which will be the default functionality if you don't put your custom override method.
Related
I'm following a tutorial to better understand Natural Ordering, using TreeSet and the Comparable interface.
The tutorial tells me that, to add non-primitive custom objects to Sets, I need to implement equals() and hashCode(). However, even without implementing these methods I'm able to compile and run the code (as below). I am using IntelliJ with Java 8.
Is overriding equals() and hashCode() absolutely necessary when working with TreeSets (SortedSet interface) and natural ordering?
class My_Person implements Comparable<My_Person>{
private String name;
public My_Person(String name) {
this.name = name;
}
public String toString() {
return name;
}
// #Override
// public boolean equals(Object o) {
// if (this == o)
// return true;
// if (o == null || getClass() != o.getClass())
// return false;
// My_Person my_person = (My_Person) o;
// return Objects.equals(name, my_person.name);
// }
//
// #Override
// public int hashCode() {
// return Objects.hash(name);
// }
#Override
public int compareTo(My_Person person) {
return name.compareTo(person.name);
}
}
public class NaturalOrdering {
public static void main(String[] args) {
List<My_Person> list = new ArrayList<>();
Set<My_Person> set = new TreeSet<>();
addElement(list);
addElement(set);
Collections.sort(list);
showElement(list);
System.out.println("\n");
showElement(set);
}
private static void addElement(Collection<My_Person> collection) {
collection.add(new My_Person("Joe"));
collection.add(new My_Person("Sue"));
collection.add(new My_Person("Juliet"));
collection.add(new My_Person("Clare"));
collection.add(new My_Person("Mike"));
}
private static void showElement(Collection<My_Person> collection) {
for(My_Person element: collection) {
System.out.println(element);
}
}
}
This depends on your requirements for equality. If you don't override equals and hashCode, two objects are defined as equal if and only if they are identical (i.e. the same object). If you need some other definition for equality you must override the methods.
It is not "absolutely necessary", but then you may get unexpected/wrong output if you don't do so. So if you don't override them, it may still work sometimes, but it may fail too. So just to be safe, better override it.
It will fail if you need to check equality. But if you are only concerned with sorting of the stored objects as per your own defined logic in compareTo method, then I think there is no need to override equals or hashcode.
Its not required by TreeSet or for that matter any Set. But Set is by nature collection of unique objects. For primitive types, Java has way to know whether two objects are same or not. For non-primitive user has to tell Java on how to know whether two objects are same or not and way is to override equals and hashcode methods which are invoked by Set#add method to determine the object being added already exists in the set or not. So if you need a functional unique objects in your Set, you should implement equals and hashcode methods. Hope this helps. This is a good read on the same topic.
You have the source code for TreeSet (and TreeMap which TreeSet uses under the hood). You can clearly see that TreeSet (and TreeMap) rely on compareTo() and not hashCode().
On the other hand, the HashSet (and HashMap which HashSet uses under the hood) does use hashCode() and equals(). No big surprise, considering that they are named HashSet and HashMap.
Here is the code which produces hashset size 3 instead 2
package dump.test;
import java.util.*;
public class WrappedString {
private String s;
public WrappedString(String s) { this.s = s; }
public static void main(String[] args) {
HashSet<Object> hs = new HashSet<Object>();
WrappedString ws1 = new WrappedString("aardvark");
WrappedString ws2 = new WrappedString("aardvark");
String s1 = new String("aardvark");
String s2 = new String("aardvark");
hs.add(ws1); hs.add(ws2); hs.add(s1); hs.add(s2);
System.out.println(hs.size()+hs.toString());
}
public boolean equals(Object aSong) {
String s = aSong.toString();
System.out.println(s);
return s.equals(this.s);
}
public int hashCode() {
System.out.println(this.s + "-" + this.s.hashCode());
return this.s.hashCode();
}
/*public int compareTo(Object aSong) {
String s = aSong.toString();
return this.s.compareTo(s);
}*/
}
It always print below output if equals and hashCode are overridden
you can see both objects having same code in output but counted as different and produced count as 3
this is if we do not override equals and hashCode
Please assist me how this works.
The problem is that your case is not symmetric. If the implementation decides to invoke equals on the String instance with your class as an argument, it will definitely return false and thus your code will not always work.
From the JavaDoc for the Set interface:
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.
So, HashSet checks the equals(Object) before adding the new element. As the second String is equal to the first, it's not added. On your WrappedString, you are not overriding the equals(Object), so, you are using the one inherited from Object, which simply checks the object IDs (659e0bfd and 2a139a55 in your case).
adding
public String toString() {
return this.s;
}
cleared my confusion. Previously it couldn't convert true value from casting object to string and needed to override toString to return underlying value.
I am trying to write a custom hashCode fn, but I am not able to figure out the correct way to do that.
public class Person {
String name;
List<String> attributes;
#Override
public boolean equals(Object o) {
// Persons are equal if name is equal & if >= 2 of attributes are equal
// This I have implemented
}
#Override
public int hashCode() {
final int PRIME = 59;
int result = 1;
result = (result*PRIME) + (this.name == null ? 0 : this.name.hashCode());
//Not sure what to do here to account for attributes
return result;
}
}
I want the hashCode fn to be such that:
"If object1 and object2 are equal according to their equals() method, they must also have the same hash code"
Not sure how to do that?
As Oli points out in the comments, you cannot solve this by implementing equals() and relying on a Set to de-duplicate for you. Weird things could happen.
Thus you must resort to coding this yourself. Add the first item from your list into your new de-duplicated list. Then for each remaining item in your original list, compare it with those already present in your de-duplicated list and only add it if it passes your non-duplicate test.
Easiest way to fulfill the contract of the equals/hashcCode methods is to return a constant:
#Override
public int hashCode() {return 13;}
Otherwise your solution with a hash code based only on name will work.
I am using a Tree Set to store some Signals as objects in my Tree Set and also want to update an object If the same signal comes again. So far I tried something but the problem is I am not able to get the complete object when I try to print it and Secondly I don't know if there is any way to update an abject and save it back to the set...
Here is my code
Signal Class (Signal.java)
public class Signal implements Comparable<Signal>{
String source;
String name;
int occurance;
public void setSource(String source){
this.source = source;
}
public void setName(String name){
this.name = name;
}
public void setOccurance(int occurance){
this.occurance = occurance;
}
public String getSource(){
return this.source;
}
public String getName(){
return this.name;
}
public int getOccurnace(){
return this.occurance;
}
#Override
public int compareTo(Signal arg0) {
// TODO Auto-generated method stub
return 0;
}
}
My Main Class
public class SortedSetTest {
public static void main(String[] args) {
// Create the sorted set
SortedSet<Signal> set = new TreeSet<Signal>();
//Create a Signal object for each new signal
Signal sig = new Signal();
sig.setSource("Source");
sig.setName("Signal Name");
sig.setOccurance(1);
// Add elements to the set
set.add(sig);
System.out.println(set);
The above print show me as object...How Can I see the set as String?
// Iterating over the elements in the set
Iterator<Signal> it = set.iterator();
while (it.hasNext()){
Here I want to print each object
For example take the first object and print the object (Signal) Source, Name and occurance and so on till the end of the set reaches.
}
}
}
The lines you're looking for:
while (it.hasNext()){
Signal sig = (Signal)it.next();
System.out.println(sig.getName());
System.out.println(sig.getOccurance());
// do the same with source or whatever property
}
You need to override toString in your Signal object to obtain a string that is more user-friendly than the default implementation in the Object class.
Also note that because all your Signals are equal based on your implementation of compareTo (always return 0) you won't be able to add more than one Signal to your set.
You are correctly printing the set.
You just haven't implemented the Signal.toString() method to represent a Signal object the way you want it.
About updating objects in a TreeSet: the object must not be modified in such a way that the output of compareTo is changed. If it needs to be updated in this way, remove it (using Set.remove(object), update it and add it back.
That said, your compareTo() method always returns 0. It should return arg0.occurence - occurence if you want higher occurence values to come before lower ones.
This is because returning a value less than 0 means this comes before the argument. 0 means equal ordering and > 1 means this comes after the argument.
Also, if you implement compareTo() you define the natural ordering of the class. It is strongly recommended to also override equals() and return true if and only if compareTo() returns 0. See http://docs.oracle.com/javase/7/docs/api/java/lang/Comparable.html (search for 'consistent with equals'). With the above implementation of compareTo(), this would mean returning true only when they both have the same occurence value. If you don't want to implement equals this way (it is probably not correct) then you should write a class that implements Comparator<Signal> and pass that to the TreeSet constructor.
First of all you need to override toString method,insteadof using mutator methods try to use a constructor which implements all your instance variables and the third thing is TreeSet uses a
Red-Black tree structure,and guarantees that the elements will be ascending order,according to the natural order.So in compareTo() method you can compare one of your variables to get the solution.
Basically, I have a Container class called "Employees" which has in it an ArrayList. This ArrayList contains "Employee" objects, which in turn contain "EmployeeData" objects which in turn contain String objects such as "first" or "last" (which are employee names).
Here's a diagram of the ArrayList structure:
ArrayList[Employee] emps ==> 1:Many ==> Employee emp
Employee emp ==> 1:1 ==> EmployeeData data
EmployeeData data ==> 1:2 ==> String last // A string that contains employee's last name.
How in the world would I perform a quicksort on the ArrayList so that the "Employee" objects in it are in alphabetical order based on the String object "last"? It seems kinda complicated!
Here's a basic design of my classes:
class Employees{
//data:
private ArrayList<Employee> emps = new ArrayList<Employee>();
//Some constructors go here
//Methods to add, remove, toString, etc, go here
public /*output a sorted ArrayList?*/ sort(){
// Some kind of "quicksort" in here to modify or create a new ArrayList sorted by employee's las name...
}
}
class Employee{
//data:
EmployeeData data;
// Some methods to construct and modify EmployeeData data.
}
class EmployeeData{
//data:
String first, last; // I wish to sort with "last". How do you do it?
double payrate, hours;
//...methods...
}
As you can see, those are the classes. I have no idea how to implement "sort" in the "Employees" class so that it sorts the ArrayList by the "last" variable of the "EmployeeData" class.
You can make a comparator, something like:
public class MyComparator implements Comparator<Employee>
{
public int compare(Employee e1, Employee e2)
{
return e1.getData().getLast().compareTo(e2.getData().getLast());
}
}
Then use it to sort the list.
Collections.sort(myList, new MyComparator());
Alternatively, you can use a TreeSet to sort on insertion using this comparator or make the Employee a comparable object to sort using Collections or a SortedSet.
public class Employee implements Comperable<Employee>
{
...
public int compareTo(Employee e)
{
return this.getData().getLast().compareTo(e.getData().getLast());
}
...
}
Define Employee implements Comparable<Employee>.
In the compareTo method, dig into the layers and compare the strings you need. Then you can use Collections.sort(), or you can store the data in a SortedSet, which is naturally ordered.
The best practice is to encapsulate the sorting logic in the class stored in the ArrayList, Employee in this case. Implement Comparable by creating a compareTo(Employee) method.
import java.util.*;
public class Employee implements Comparable<Employee> {
public EmployeeData Data;
public Employee(String first, String last)
{
Data = new EmployeeData(first, last);
}
public int compareTo(Employee other)
{
return Data.Last.compareTo(other.Data.Last);
}
public String toString() {
return Data.First + " " + Data.Last;
}
public static void main(String[] args) throws java.io.IOException {
ArrayList list = new ArrayList();
list.add(new Employee("Andy", "Smith"));
list.add(new Employee("John", "Williams"));
list.add(new Employee("Bob", "Jones"));
list.add(new Employee("Abraham", "Abrams"));
Collections.sort(list);
for (int i = 0; i < list.size(); i++)
{
System.out.println(list.get(i));
}
System.in.read();
}
}
public class EmployeeData {
public String First;
public String Last;
public EmployeeData(String first, String last)
{
First = first;
Last = last;
}
}
Output:
Abraham Abrams
Bob Jones
Andy Smith
John Williams
Peter DeWeese and others have given you very good answers. You can use
Collections.sort(myList, new MyComparator());
to sort myList using a Comparator you have defined. <=== What the heck does that mean?
In Java, if something implements Comparable (java.lang.comparable) then you can define an order for your elements. It seems like you know what Java Generics are, as you used them to declare your ArrayList as being of type < Employee >. This is awesome, because you can store an Employee object into each entry in the ArrayList. So far so good?
However, if you want to sort objects, first you have to define an order. Since objects can have various properties, maybe I want to sort my employees by ear-size. In this case, I simply tell Java that my class implements Comparable. With generics, I have to specify that it implements Comparable< Employee > because I am defining an order for my Employee objects (peons, minions, whatever).
Peter DeWeese mentioned:
public int compareTo(Employee e)
{
return this.getData().getLast().compareTo(e.getData().getLast());
}
and Jason Goemaat mentioned:
public int compareTo(Employee other)
{
return Data.Last.compareTo(other.Data.Last);
}
What the heck does this mean? If I say that my class implements Comparable then I need to define a compareTo function. (An interface is a collection of methods that need to be implemented) The function compareTo defines the order of my elements.
From the Comparable< T> spec:
int compareTo(T o)
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.
If I am comparing ear sizes, and let's say I want big ears to come first in my list, then I could (re)define compareTo as:
public int compareTo(Employee e)
{
if (this.earSize > e.earSize) //big ears come first
return -1;
if (this.earSize == e.earSize) //equality
return 0;
else
return 1; // if e.earSize > this.earSize then return 1
}
To answer Steve Kuo's question, we put the keyword this in our comparator because when we call the compareTo method
x.compareTo(y);
the keyword this will refer to x.
You can think of compareTo as being a method of the object x, so when you call x.compareTo(y) you are really saying this.compareTo(y) from within the scope of object x.
We can also look at a String example:
This means that if I want "Medvedev" to come before "Putin" (as 'M' comes before 'P' in the English alphabet) I would have to state that I want compareTo to return -1 when comparing Medvedev to Putin.
String TheMString = "Medvedev";
String ThePString = "Putin";
then the line
TheMString.compareTo(ThePString);
will evaluate to -1.
Now a standard routine such as Collections.sort(list, comparator) will be able to use these values that compareTo returns to figure out the [absolute] order of list. As you may know, sorting is a comparison based operation and we need to know what value is "less than" or "greater than" another value in order to have a meaningful sort.
One big caveat is that if you call compareTo on Strings, it defaults to alphabetical order, so you may simply tell compareTo to return A.compareto(B) and it will make sure the strings are in order.
Normally (well, I should say, in other cases) when redefining the compareTo method, you must explicitly state a neg/zero/pos return value.
I hope that helps.