Java comparable interface compareTo(Object o), what is object o? - java

Beginner, can't seem to wrap my head around this.
Item Class
public class Item implements Comparable {
private int number;
private String description;
public Item(int num, String descript){
this.number = num;
this.description = descript;
}
public int getNumber(){
return number;
}
public String getDescription(){
return description;
}
#Override public int compareTo(Object o){
Item i = (Item) o;
if(this.getNumber() < i.getNumber())
return -1;
if(this.getNumber() > i.getNumber())
return 1;
return 0;
}
}
Main method
Item[] items = new Item[3];
items[0] = new Item(102, "Duct Tape");
items[1] = new Item(103, "Bailing wire");
items[2] = new Item(101, "Chewing Gum");
Arrays.sort(items);
for (Item i : items){
System.out.println(i.getNumber() + ": " + i.getDescription());
}
When the main method instantiate items[0] = new Item(102, "Duct Tape"); Which goes through Item constructor to set current variable number, description.
My problem is I can't seem to understand what is being passed in the compareTo argument, therefore I can't seem to understand this.getNumber() < i.getNumber() is doing...
Return a negative number if current object is less than passed object?
any help is much appreciated.

The raw interface Comparable that you are using allows this object to be comparable to another Object. It is then cast to Item to ensure that the this Item is only compared to another Item.
According to the Comparable Javadocs,
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.
Additionally, your compareTo will throw a ClassCastException at runtime if the Object isn't an Item.
It's better to use the generic form of the interface:
public class Item implements Comparable<Item> {
Then you can specify Item instead of Object to compare against:
#Override public int compareTo(Item i){
and you don't have to cast the Object to an Item. When calling compareTo, the compiler will enforce that the object to compare to must be an Item.

My problem is I can't seem to understand what is being passed in the compareTo argument
Array.sort uses the compareTo method to know how to compare pairs of items from the array.
Your sort method assumes that it will only be called with instances of Item, and that's all Array.sort will pass along to compareTo assuming that your array only holds instances of Item.
You will get a runtime exception if compareTo is ever invoked with the item being compared to not being an instance of Item.
I can't seem to understand this.getNumber() < i.getNumber() is doing...
It's comparing two instance of Item based on the value of getNumber, that is, the value of their number fields. It assumes that two instances of Item are equal if Item.number is the same for each instance, otherwise one is less than the other if its Item.number is less than the other's Item.number.

You are trying to override the default compareTo() method, which takes in a single Object as a parameter. Thus, in your implementation, the parameter needs to be of type Object. However, you assume that you will be passing in a Item object, so you can cast it accordingly.
So in the end, your Item object is treated like an Object, which is casted back to an Item in your compareTo() method.

The Object being passed to your compareTo method is an Item object. The Arrays.sort() calls the Overrided method method when you have it implemented, the best implementation is to return the compareTo method for the primitive java types
#Override
public int compareTo(Object o){
return (this.getNumber().compareTo((Item)o.getNumber());
}
(this requires you to have number as type Integer though)

Related

What type to return?

Consider a method which produces different types of results. In my case it's either an ArrayList or an Integer (pseudo code):
int a = ... // value for a comes from another function
public ArrayList compute(){ // return either ArrayList or Integer
if(a==1){
ArrayList result = new Arraylist()
for(int i=0; i<=something; i++){
arr.add(...);
}
}
if(a==2){
int result;
result = somethingElse;
}
return result;
}
Depending on the result of a, the result of result comes either from a loop and loads the results into an ArrayList, or in the second case it will just be a single number.
What type should the method return?
Return a List<Integer>. For a single integer simply return a list with a single element.
An alternative to returning a List (but "functionally" the same),
public void compute(List<Integer> result){
// add/remove/set the given list,
}
And although this looks like a bad design in general, you may in this case actually return a value that indicates if a "list" or a single value (a list with one element) is returned.
public boolean compute(List<Integer> result){ ...
Or, better, the length of the list (depends on what you're really trying to achieve):
public int compute(List<Integer> result){
...
return result.size();
}
You can change the signature of the method to be public Object compute(), so that you can return both ArrayLists and Integers, but I'm not exactly sure why you'd want to do this.
It just means that whenever you call compute(), you're going to need to check the type of the Object that you received, e.g.
Object result = compute();
if(result instanceof ArrayList) {
// Do ArrayList stuff
} elseif(result instanceof Integer) {
// Do Integer stuff
}
Note: Object is the super class for all objects in Java, so if there is a time where you may want to return lots of different things, you can use Object. But the better solution may be to create an Interface, if the things you're returning will have something in common.
See here: http://docs.oracle.com/javase/tutorial/java/concepts/interface.html

Comparable and Comparator Interface

As I was going through the above interfaces I am not much clear about the syntax of these Interfaces after reading many sites on same topic.
Consider the following code snippet :
public class ComparableTest implements Comparable,Comparator {
String name;
int age;
public ComparableTest(String name,int age){
this.name=name;
this.age=age;
}
#Override
public int compareTo(Object o){ // line 1
ComparableTest c=(ComparableTest)o;
return name.compareTo(c.name); // line 2
}
#Override
public int compare(Object o1, Object o2){ // line 4
ComparableTest c1=(ComparableTest)o1;
ComparableTest c2=(ComparableTest)o2;
return return c1.name.compareTo(c2.name);
}
public static void main(String[] args) {
ComparableTest ct1=new ComparableTest("Max",23);
ComparableTest ct2=new ComparableTest("Alex",22);
ComparableTest ct3=new ComparableTest("Zen",25);
List lt=new ArrayList();
lt.add(ct1);
lt.add(ct2);
lt.add(ct3);
Collections.sort(lt); // line 3
Collections.sort(lt,new ComparableTest("jack",98)); // line 5
}
}
1) In line 3, Collections.sort(lt) with list as parameter calls compareTo which is having Object as parameter accepts the list lt. How ? Isn't the compareTo must have a List as Parameter. I know List is also type of Object but how Object o will accepts that List which contains an instances of some class.(Please exclude generics as i don't know now)
2) Suppose I call c1.comapreTo(c2) then it is clear c1 is this object and c2 is another object for comparison. So then in comapreTo(Object o) the following line is crystal clear
public int compareTo(Object o) {
//cast the Object o
return c1.name.compareTo(c2.name);
}
But In line 2, I wrote only name.compareTo(c.name) and compared. What does name refer here. How Sorting is taking place? I read that it is current object but we call compareTo with Collections.sort not with any object.
3) As line 5 calls line 4, line 4 takes list lt as o1 and newly Object created as o2. I don't understand the object to be sorted are in list then why we are passing some different object and comparing with it as it is not in list and will not be included in our result also. How Sorting is taking place here?
4) What different values we can pass for second parameter in line 5 ?
Would be appreciable if make each query understandable.
In line 3 Collections.sort takes a list as parameter and compareTo(object o) is executed by the objects of the list taking as argument other objects of the list.
In line 2 name is the atribute name of the object who executes the method.
When you call collections.sort(lt) compareTo(Object o) is used to do the orderig and is executed by several objects of the list taking other objects of the list as arguments, it depends of the shorting algoritm that collections.sort() uses.
For example in a list l with only two objects calling collections.sort(l) will make compareTo(Object o) be executed by one object taking the other as argument and the list will be in order. Bigger list will require more calls.
collections.sort() acepts one parameter a list or a list and a Comparator.

Custom hashCode when using part of a List for comparision

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.

Sorting ArrayList<CustomObject>

I'm facing problem while trying to sort an ArrayList of custom object. In fact, after the sorting, nothing has change in my ArrayList. Is something wrong with my method?
Here's my Artist custom object property :
public class Artist {
String mNickname;
String mName;
String mDescription;
String mScene;
String mDay;
String mTime;
String mImageURL;
Date mDate;
// getters and setters below like getDate() for mDate...
And here's the method use to sort :
static public ArrayList<Artist> sortArrayByDate(ArrayList<Artist> list) {
Collections.sort(list, new Comparator<Artist>() {
#Override
public int compare(Artist lhs, Artist rhs) {
if (lhs.getDate().getTime() < rhs.getDate().getTime())
return -1;
else if (lhs.getDate().getTime() == rhs.getDate().getTime())
return 0;
else
return 1;
}
});
return list;
}
I know this topic as been discuss many time on StackOverflow, but I can't find why I'm not able to make it work properly. Thanks for your understanding
EDIT : Dates (java.util.date) are create using SimpleDateFormatter
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.CANADA_FRENCH);
Why not simply use Date#compareTo() for the comparison since java.util.Date implements the Comparable interface.
It is not necessary for the method to return an instance of the List after it is sorted because the underlying List object will be modified by invoking sort. Basically, the method is invoked by passing a reference value as an argument. So when modifications are made, the changes are reflected on the underlying object pointed to by the reference value. With this approach, the code simply passes the List into the method and then continues using the same reference in the proceeding code, which will point to an underlying List which has been sorted.
Another item to consider is modifying the method to accept an argument of the type Listas opposed to ArrayList, since List is an interface, hence more abstract. This would allow the code to switch the implementation of List being passed to the method. This is important because ArrayList does not guarantee the order of the items in the list is maintained. To guarantee the order of items in the List is maintained used LinkedList.
static public void sortArrayByDate(List<Artist> list) {
Collections.sort(list, new Comparator<Artist>() {
#Override
public int compare(Artist lhs, Artist rhs) {
return lhs.getDate().compareTo(rhs.getDate());
}
});
}
Here is a GitHub Gist I created, that show this method in action, with a complete working example.

How do I write a compareTo method which compares objects?

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);
}
}

Categories