Comparable and Comparator Interface - java

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.

Related

How to create an equals method that compares two object arrays of a class?

I am trying to create a equals method that compares two objects. The thing is, I'm a bit a of new to this stuff so I'll try to explain my goal as easy as possible.
public class A {
...
}
public class B {
private A[] arr = new A[10];
public boolean equals(A[] temp) {
//compare
}
}
Assume the code above is a summary of what I have. Now, assume I had: arr.equals(Obj)
Obj being another A[] object. Now in my equals statement, I want to reference the original arr array, how do I go about doing that?
For example, let's say I wanted to compare arr's length to temp's length (aka Obj's length), how would I do that? I know it would be something like (temp.length == arr.length) but how do I access arr when I pass it through by doing arr.equals(obj)?
EDIT: Just to clarify, assume the objects aren't simple arrays. So for instance, class A could have a Name, a Type (Both Strings) and possibly a Quantity (an int), so I wouldn't be able to simply compare them like they're two normal arrays.
Thanks!
You can use java.util.Arrays.equals(Object[] a, Object[] a2) which tests if the two specified arrays of Objects are equal to one another
Use the keyword this, which always represents the object you are applying the method to (immediately before the dot). For example:
public boolean equals(A[] temp) {
return this.length == temp.length ;
}
Now, in the particular case of your code, you are not defining method equals as part of class A, but of a class B whose instances contain arr. Then, the solution would be:
public boolean equals(A[] temp) {
return this.arr.length == temp.length ;
}
Write a equals mwthod in your class A
public class A {
...
//Override equals method.
}
Now if you want to compare 2 arrays of class A you can simply use java.utils.Arrays.equals(A a1[], A a2[]);
You have to override equals method in class A coz java.utils.Arrays.equals internally uses class A's equals.
Here is an example, go through it.

ArrayList Generic Sorter

I want to pass in an arraylist and sort it no matter whats in it
Error I am getting: Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Character
ArrayList myArray = new ArrayList();
myArray.add("wayne");
myArray.add("bob");
myArray.add('h');
myArray.add(4);
myArray.add(5.50);
ArrayList sortedArray = Validate.Sort(myArray);
for(Object x: sortedArray)
{
System.out.println(x);
}
public static ArrayList Sort(ArrayList value)
{
Collections.sort(value);
return value;
}
Your ArrayList has a mix of String and char values. So when the two try to get compared, the String's compareTo method is being called. It has a signature of compareTo(String,String), so it's trying to cast the char to a String. Either put all Strings in, or write a custom comparator that handles mixed types like this.
Take a look at Comparator:
http://docs.oracle.com/javase/6/docs/api/java/util/Comparator.html
In your case the compareTo() may look like:
public int compareTo(Object a, Object b){
if(a == b)
return 0;
if(a==null)
return 1;
if(b==null)
return -1;
return a.toString().compareTo(b.toString);
}
But I think this would be kind of useless.
I want to pass in an arraylist and sort it no matter whats in it
What is this supposed to mean? A total ordering is a binary relation which satisfies some properties (antisimmetry, transitivity, totality). More easily, given two elements, you must identify a criterion to determine which is smaller/bigger than the other.
You cannot do this, unless you know what these elements actually are. If you want to sort an array of whatever, given object A and B, whose types you don't even know, how can you tell which is bigger than the other?
In Java, you might use the hashCode, which is indeed a criterion, but it's hardly useful. If you want to avoid duplicates, you can use a Set rather than a List. Anyway, if you really want to sort elements based on their hashCode, you can do it like this:
private static final Comparator<Object> hashComparator
= new Comparator<Object>() {
#Override
public int compare(Object a, Object b) {
return System.identityHashCode(a) - System.identityHashCode(b);
}
#Override
public boolean equals(Object obj) {
//this is a static singleton, nothing can be equal to it
return false;
}
};
public static ArrayList Sort(ArrayList value)
{
Collections.sort(value, hashComparator);
return value;
}
EDIT: if you are interested in sorting a list of just Strings and Characters (or if you want to sort items based on their string representation) the right thing to do is probably convert everything to strings, or use a SortedMap<String, Object>. Otherwise, you can do the same I just showed you before, but using a more specific compare function, such as:
public int compare(Object a, Object b) {
return a.toString().compareTo(b.toString());
}

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

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)

Not sure how to sort an ArrayList based on parts of Objects in that ArrayList (Java)

I have a Sorts class that sorts (based on insertion sort, which was the assignment's direction) any ArrayList of any type passed through it, and uses insertion sort to sort the items in the list lexicographically:
public class Sorts
{
public static void sort(ArrayList objects)
{
for (int i=1; i<objects.size(); i++)
{
Comparable key = (Comparable)objects.get(i);
int position = i;
while (position>0 && (((Comparable)objects.get(position)).compareTo(objects.get(position-1)) < 0))
{
objects.set(position, objects.get(position-1));
position--;
}
objects.set(position, key);
}
}
}
In one of my other files, I use a method (that is called in main later) that sorts objects of type Owner, and we have to sort them by last name (if they are the same, then first name):
Directions: "Sort the list of owners by last name from A to Z. If more than one owner have the same last name, compare their first names. This method calls the sort method defined in the Sorts class."
What I thought first was to get the last name of each owner in a for loop, add it to a temporary ArrayList of type string, call Sorts.sort(), and then re-add it back into the ArrayList ownerList:
public void sortOwners() {
ArrayList<String> temp = new ArrayList<String>();
for (int i=0; i<ownerList.size(); i++)
temp.add(((Owner)ownerList.get(i)).getLastName());
Sorts.sort(temp);
for (int i=0; i<temp.size(); i++)
ownerList.get(i).setLastName(temp.get(i));
}
I guess this was the wrong way to approach it, as it is not sorting when I compile.
What I now think I should do is create two ArrayLists (one is firstName, one is LastName) and say that, in a for loop, that if (lastName is the same) then compare firstName, but I'm not sure if I would need two ArrayLists for that, as it seems needlessly complicated.
So what do you think?
Edit: I am adding a version of compareTo(Object other):
public int compareTo(Object other)
{
int result = 0;
if (lastName.compareTo(((Owner)other).getLastName()) < 0)
result = -1;
else if (lastName.compareTo(((Owner)other).getLastName()) > 0)
result = 1;
else if (lastName.equals(((Owner)other).getLastName()))
{
if (firstName.compareTo(((Owner)other).getFirstName()) < 0)
result = -1;
else if (firstName.compareTo(((Owner)other).getFirstName()) > 0)
result = 1;
else if (firstName.equals(((Owner)other).getFirstName()))
result = 0;
}
return result;
}
I think the object should implement a compareTo method that follows the normal Comparable contract--search for sorting on multiple fields. You are correct that having two lists is unnecessary.
If you have control over the Owner code to begin with, then change the code so that it implements Comparable. Its compareTo() method performs the lastName / firstName test described in the assignment. Your sortOwners() method will pass a List<Owner> directly to Sorts.sort().
If you don't have control over Owner, then create a subclass of Owner that implements Comparable. Call it OwnerSortable or the like. It accepts a regular Owner object in its constructor and simply delegates all methods other than compareTo() to the wrapped object. Its compareTo() will function as above. Your sortOwners() method will create a new List<OwnerSortable> out of the Owner list. It can then pass this on to Sorts.sort().
Since you have an ArrayList of objects, ordinarily we would use the Collections.sort() method to accomplish this task. Note the method signature:
public static <T extends Comparable<? super T>> void sort(List<T> list)
What's important here is that all the objects being sorted must implement the Comparable interface, which allows objects to be compared to another in numerical fashion. To clarify, a Comparable object has a method called compareTo with the following signature:
int compareTo(T o)
Now we're getting to the good part. When an object is Comparable, it can be compared numerically to another object. Let's look at a sample call.
String a = "bananas";
String b = "zebras";
System.out.println(a.compareTo(b));
The result will be -24. Semantically, since zebras is farther in the back of the dictionary compared to bananas, we say that bananas is comparatively less than zebras (not as far in the dictionary).
So the solution should be clear now. Use compareTo to compare your objects in such a way that they are sorted alphabetically. Since I've shown you how to compare strings, you should hopefully have a general idea of what needs to be written.
Once you have numerical comparisons, you would use the Collections class to sort your list. But since you have your own sorting ability, not having access to it is no great loss. You can still compare numerically, which was the goal all along! So this should make the necessary steps clearer, now that I have laid them out.
Since this is homework, here's some hints:
Assuming that the aim is to implement a sort algorithm yourself, you will find that it is much easier (and more performant) to extract the list elements into an array, sort the array and then rebuild the list (or create a new one).
If that's not the aim, then look at the Collections class.
Implement a custom Comparator, or change the object class to implement Comparable.

In Java, How do you quicksort an ArrayList of objects in which the sorting field is multiple layers deep?

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.

Categories