currently I'm trying to find out how java's interface "Comparable" works.
As far as I know inteface cannot have any non-static (besides default ones) methods so when we implement an interface we need to define its methods first.
But when I implement "Comparable" interface I obviously can use its compareTo method. Where is that method defined?
public static <T extends Comparable<T>> int countGreaterThan(T[] anArray, T elem) {
int count = 0;
for (T e : anArray)
if (e.compareTo(elem) > 0)
++count;
return count;
}
How come that I'm able to use interface's method without even defining it?
My apoligies if there was already an answer to that question, wasn't able to find it though.
Thx.
The elements of your T[] array must be of some type that implements Comparable<T> (as a result of the <T extends Comparable<T>> type bound). Therefore you can call compareTo() on elements of that array.
Where compareTo is implemented depends on which array you are passing to your method. If, for example, you pass a String[], the compareTo method of the String class will be used.
To your code:
You have defined a type T as a sub-class of "Comparable" within your signature and it is used within the parameters as you pass in an array of "T" ( T[] ). The compiler now knows that any T must be at least a Comparable.
Further information:
In general I do not suggest to implement Comparable directly. The reason for that is that comparison is always within a context you do not know.
You should favor the Comparator-interface over the Comparable-interface to implement comparisons with a specific contexts. If you want to define a natural order you can provide a natural comparator as a public constant.
Some examples:
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class ComparatorExample {
public static void main(String[] args) {
List<SomeObject> list = new ArrayList<>();
list.add(new SomeObject(1, "dhjf", "A"));
list.add(new SomeObject(4, "ghdg", "A"));
list.add(new SomeObject(6, "uztzt", "B"));
list.add(new SomeObject(1, "jhgf", "C"));
list.add(new SomeObject(3, "vbbn", "A"));
list.add(new SomeObject(99, "cvcxc", "A"));
list.add(new SomeObject(2, "dfdd", "G"));
// examples
Collections.sort(list, SomeObject.NATURAL);
Collections.sort(list, LexicographicOrderByCategoryAndName.INSTANCE);
LexicographicOrderByName.INSTANCE.compare(new SomeObject(99, "cvcxc", "A"), new SomeObject(54, "fdjnn", "C"));
}
public static class SomeObject {
public static Comparator<SomeObject> NATURAL = new Comparator<SomeObject>() {
#Override
public int compare(SomeObject arg0, SomeObject arg1) {
return arg1.getId() - arg0.getId();
}
};
private int id;
private String name;
private String category;
public SomeObject(int id, String name, String category) {
this.id = id;
this.name = name;
this.category = category;
}
public int getId() {
return id;
}
public String getName() {
return name;
}
public String getCategory() {
return category;
}
}
public static class LexicographicOrderByName implements Comparator<SomeObject> {
public static LexicographicOrderByName INSTANCE = new LexicographicOrderByName();
private LexicographicOrderByName() {
}
#Override
public int compare(SomeObject o1, SomeObject o2) {
return o1.getName().compareTo(o2.getName());
}
}
public static class LexicographicOrderByCategoryAndName implements Comparator<SomeObject> {
public static LexicographicOrderByCategoryAndName INSTANCE = new LexicographicOrderByCategoryAndName();
private LexicographicOrderByCategoryAndName() {
}
#Override
public int compare(SomeObject o1, SomeObject o2) {
int c = o1.getCategory().compareTo(o2.getCategory());
if (c == 0) {
c = o1.getName().compareTo(o2.getName());
}
return c;
}
}
}
The problem with the Comparable-interface is that once you decided a specific comparison of an object you are bound to it forever. You may may change it but expect side effects as you do not know if all contexts have the same erasure and should change too.
Related
I am not sure how to implement a comparable interface into my abstract class. I have the following example code that I am using to try and get my head around it:
public class Animal{
public String name;
public int yearDiscovered;
public String population;
public Animal(String name, int yearDiscovered, String population){
this.name = name;
this.yearDiscovered = yearDiscovered;
this.population = population; }
public String toString(){
String s = "Animal name: "+ name+"\nYear Discovered: "+yearDiscovered+"\nPopulation: "+population;
return s;
}
}
I have a test class that will create objects of type Animal however I want to have a comparable interface inside this class so that older years of discovery rank higher than low. I have no idea on how to go about this though.
You just have to define that Animal implements Comparable<Animal> i.e. public class Animal implements Comparable<Animal>. And then you have to implement the compareTo(Animal other) method that way you like it.
#Override
public int compareTo(Animal other) {
return Integer.compare(this.year_discovered, other.year_discovered);
}
Using this implementation of compareTo, animals with a higher year_discovered will get ordered higher. I hope you get the idea of Comparable and compareTo with this example.
You need to:
Add implements Comparable<Animal> to the class declaration; and
Implement a int compareTo( Animal a ) method to perform the comparisons.
Like this:
public class Animal implements Comparable<Animal>{
public String name;
public int year_discovered;
public String population;
public Animal(String name, int year_discovered, String population){
this.name = name;
this.year_discovered = year_discovered;
this.population = population;
}
public String toString(){
String s = "Animal name: "+ name+"\nYear Discovered: "+year_discovered+"\nPopulation: "+population;
return s;
}
#Override
public int compareTo( final Animal o) {
return Integer.compare(this.year_discovered, o.year_discovered);
}
}
While you are in it, I suggest to remember some key facts about compareTo() methods
CompareTo must be in consistent with equals method e.g. if two objects are equal via equals() , there compareTo() must return zero otherwise if those objects are stored in SortedSet or SortedMap they will not behave properly.
CompareTo() must throw NullPointerException if current object get compared to null object as opposed to equals() which return false on such scenario.
Read more: http://javarevisited.blogspot.com/2011/11/how-to-override-compareto-method-in.html#ixzz4B4EMGha3
Implement Comparable<Animal> interface in your class and provide implementation of int compareTo(Animal other) method in your class.See This Post
You would need to implement the interface and define the compareTo() method.
For a good tutorial go to - Tutorials point link
or
MyKongLink
Emp class needs to implement Comaparable interface so we need to Override its compateTo method.
import java.util.ArrayList;
import java.util.Collections;
class Emp implements Comparable< Emp >{
int empid;
String name;
Emp(int empid,String name){
this.empid = empid;
this.name = name;
}
#Override
public String toString(){
return empid+" "+name;
}
#Override
public int compareTo(Emp o) {
if(this.empid==o.empid){
return 0;
}
else if(this.empid < o.empid){
return 1;
}
else{
return -1;
}
}
}
public class JavaApplication1 {
public static void main(String[] args) {
ArrayList<Emp> a= new ArrayList<Emp>();
a.add(new Emp(10,"Mahadev"));
a.add(new Emp(50,"Ashish"));
a.add(new Emp(40,"Amit"));
Collections.sort(a);
for(Emp id:a){
System.out.println(id);
}
}
}
Possible alternative from the source code of Integer.compare method which requires API Version 19 is :
public int compareTo(Animal other) {
return Integer.valueOf(this.year_discovered).compareTo(other.year_discovered);
}
This alternative does not require you to use API version 19.
Use a Comparator...
public class AnimalAgeComparator implements Comparator<Animal> {
#Override
public int compare(Animal a1, Animal a2) {
...
}
}
This thing can easily be done by implementing a public class that implements Comparable. This will allow you to use compareTo method which can be used with any other object to which you wish to compare.
for example you can implement it in this way:
public String compareTo(Animal oth)
{
return String.compare(this.population, oth.population);
}
I think this might solve your purpose.
The graphic below shows a compilation error involving my if-else conditional when attempting to implement the compareTo() method located in my Tool class. I am uncertain of the issue, as it would appear that the method is public and within my Tool class (from which the two objects being compared are constructed).
public interface Product {
public abstract String getName();
public abstract double getCost();
}
public abstract class Vehicle implements Product {
private String name;
private double cost;
public Vehicle(String name, double cost) {
this.name = name;
this.cost = cost;
}
public String getName() {
return name;
}
public double getCost() {
return cost;
}
}
public class Car extends Vehicle {
public Car(String s, double d) {
super(s, d);
}
}
public class Truck extends Vehicle {
public Truck(String s, double d) {
super(s, d);
}
}
public class Tool implements Product, Comparable<Product> {
private String name;
private double cost;
public Tool(String name, double cost) {
this.name = name;
this.cost = cost;
}
public String getName() {
return name;
}
public double getCost() {
return cost;
}
public int compareTo(Product obj) {
if (getCost() < obj.getCost()) {
return -1;
} else if (getCost() == obj.getCost()) {
return 0;
} else {
return 1;
}
}
}
import java.util.*;
public class InventoryDemo
{
public static void main(String [] args) {
ArrayList<Product> list = new ArrayList<Product>();
list.add(new Car("Jagur", 1000000));
list.add(new Car("Neon", 17000));
list.add(new Tool("JigSaw", 149.18));
list.add(new Car("Jaguar", 110000));
list.add(new Car("Neon", 17500));
list.add(new Car("Neon", 17875.32));
list.add(new Truck("RAM", 35700));
list.add(new Tool("CircularSaw", 200));
list.add(new Tool("CircularSaw", 150));
list.add(new Tool("saw1", 200));
list.add(new Tool("saw2", 150));
if(list.get(9).compareTo(list.get(10)) == 0) {
System.out.println("\nThey are the same size using compareTo().");
} else {
System.out.println("\nThey are not the same size using compareTo().");
}
}
}
The Problem is your list is of type List<Product>, but product does not implement the Comparable interface, therefore this type does not implement the method.
Make
public interface Product extends Comparable<Product> {
public abstract String getName();
public abstract double getCost();
}
Your Product interface doesn't extend Comparable<Product> which adds
int compareTo(Product other);
list is declared as ArrayList<Product>, so list.get(9) will return you Product object.
To resolve issue you have either to make Product extend Comparable<Product> and implement method in Vehicle, or, maybe, use equals() method instead, overriding default implementation. Actually the second way is preferrable, because equals() method checks whether objects are equal, while compareTo() tells you if this object is greater then other, or other is greater than this, or none of that is applicable - which makes equals() usage more semantically correct in your case.
Your list is an ArrayList<Product>, so list.get(9) returns a Product.
The compareTo(Product) method is not defined in interface Product. It's defined in class Tool, but you're trying to call it on a Product, which is not (always) a Tool.
To solve this: Make your interface Product extend Comparable<Product>:
interface Product extends Comparable<Product> {
Ofcourse that means that any (non-abstract) class that implements interface Product must also have a public int compareTo(Product obj) method.
The list item you are trying to call compareTo() on is a Product, because the list is declared as a list of Products:
ArrayList<Product> list = new ArrayList<Product>();
When accessing items in the list, Java is only aware that the items implement the Product interface, irrespective of whether the actual class also implements Comparable.
One solution is to define Product as extending Comparable:
public interface Product extends Comparable<Product> {
public abstract String getName();
public abstract double getCost();
}
In addition to the included items, I have to store the name and the id of the List inside itself. Thus i extended an ArrayList as follows:
class MyList<E> extends ArrayList<E>{
private int id;
private String name;
MyList(int id, String name){
this.id = id;
this.name = name;
}
id getId(){ return id; }
String getName(){ return name; }
}
Now I realized, that this extension will only hold one specific type of objects. So how can I remove the generic character of my list?
class MyList<MyObject> extends ArrayList<E>
class MyList<MyObject> extends ArrayList<MyObject>
...and so on fails. I want to instantiate my list by
MyList mylist = new MyList();
...and it should automatically accept only MyObjects...
(Would it be better to create a wrapper which holds an ArrayList in addition to the meta? But because it is still a list, why remove all list-typical capabilities...)
You'll need
class MyList extends ArrayList<MyObject>
When you declare a type parameter for your class declaration like so
class MyList<MyObject> ...
the type MyObject> is not your type, it is a type variable that also has the name MyObject.
What you want, is to use your MyObject type as a type argument for the ArrayList type parameter as shown above.
But, as others have suggested, composition is probably a better way to do this, ie the wrapper you suggested in your question.
As has been answered already, the correct declaration would be
class MyList extends ArrayList<MyObject>
Even though you have no interest in overriding any ArrayList methods, you should consider composition over inheritance for this type of scenarios.
Example:
class MyList implements Iterable<MyObject> {
private final int id;
private final String name;
private final List<MyObject> list;
MyList(int id, String name){
this.id = id;
this.name = name;
this.list = new ArrayList<>();
}
int getId() { return id; }
String getName() { return name; }
MyObject get(int i) { return list.get(i); }
void add(MyObject o) { list.add(o); }
void remove(MyObject o) { list.remove(o); }
void remove(int i) { list.remove(i); }
void set(int i, MyObject o) { list.set(i, o); }
boolean contains(MyObject o) { return list.contains(o); }
int size() { return list.size(); }
#Override
Iterator<MyObject> iterator() { return list.iterator(); }
}
With this:
You can easily switch the ArrayList for a LinkedList, or any other list;
You control the methods this class offers;
If you like the chaining style, you may change those void for MyList;
etc.
Is it possible to connect a string and a double in a ArrayList ?
It should look this way:
[itemA 1, itemB 10, itemC 5, ...]
I tried it with write the double also as String in the ArrayList, but then Collections.sort(list) won't sort the doubles correct.
That's when you make a class.
class MyObject {
private String someString;
private double someDouble;
// Getters & setters
}
List<MyObject> myList = new ArrayList<>();
You can just keep a list of this custom object.
If you want to be able to sort your list on the double value, simply construct your class to implement the Comparable interface.
class MyObject : Comparable<MyObject>{
private String someString;
private double someDouble;
#override
public int compareTo(MyObject o) {
return this.someDouble.compareTo(o.getSomeDouble());
}
}
You can use Pair from apache commons:
List<Pair<String, Double>> list = new ArrayList<>();
list.add(new ImmutablePair<>("STRING", 2.0));
You can create a class like, MyClass and create a String variable and double value.
Like,
class MyClass
{
private String strVlaue;
private String doubleValue;
//Constructor using fields, and getter/setter method
}
Since you want to make it in order by double Value.
You can either make MyClass implement Comparable and implement custom order in compareTo method OR create a comparator for MyClass
1 Implements Comparable
public class MyClass implements Comparable {
private String strVlaue;
private double doubleValue;
public MyClass(String strVlaue, double doubleValue) {
this.strVlaue = strVlaue;
this.doubleValue = doubleValue;
}
#Override
public int compareTo(Object o) {
MyClass mc = (MyClass) o;
return Double.compare(this.doubleValue, mc.getDoubleValue());
}
public String getStrVlaue() {
return strVlaue;
}
public void setStrVlaue(String strVlaue) {
this.strVlaue = strVlaue;
}
public double getDoubleValue() {
return doubleValue;
}
public void setDoubleValue(double doubleValue) {
this.doubleValue = doubleValue;
}
}
Use Collections.sort(List<MyClass>) to sort.
2 Keep MyClass not implement Comparable, create a comparator.
public class MyComparator implements Comparator<MyClass>{
#Override
public int compare(MyClass o1, MyClass o2) {
return Double.compare(o1.getDoubleValue(), o2.getDoubleValue());
}
}
Use Collections.sort(List<MyClass>, new MyComparator()) to sort.
A.java
public class A implements Comparable {
private String id;
private String name;
public A(String a, String b) {
id = a;
name = b;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int compareTo(Object o) {
A a = (A) o;
return id.compareTo(a.getId());
}
}
B.java
public class B implements Comparable {
private String b_id;
private String other;
public B(String a, String b) {
b_id = a;
other = b;
}
public String getBId() {
return b_id;
}
public void setBId(String id) {
this.b_id = id;
}
public String getOther() {
return other;
}
public void setOther(String other) {
this.other = other;
}
public int compareTo(Object o) {
B b = (B) o;
return b_id.compareTo(b.getId());
}
}
Learn.java
public class Learn {
public static void main(String[] args) {
List<A> listA = new ArrayList<A>();
List<B> listB = new ArrayList<B>();
List<Object> listAll = new ArrayList<Object>();
listA.add(new A("aa", "bb"));
listA.add(new A("ae", "bbn"));
listA.add(new A("dfr", "GSDS"));
listB.add(new B("nm", "re"));
listB.add(new B("asd", "asfa"));
listAll.addAll(listA);
listAll.addAll(listB);
Collections.sort(listAll);
for (Object o : listAll) {
if (o instanceof A)
System.out.println(o.getId);
else if (o instanceof B)
Syatem.out.println(o.getBId);
}
}
}
The error i get is at the line Collections.sort(listAll);
It says.
Bound mismatch: The generic method sort(List<T>) of type Collections is not applicable
for the arguments (List<Object>). The inferred type Object is not a valid substitute
for the bounded parameter <T extends Comparable<? super T>>
What to do? Also is the rest of the logic all right?
What i am trying to do is have a list of A and list of B with one attribute same as id; though the variable name is not the same. i.e id in A and bid in B.
Now i put both the lists in ListAll and do sort on them on the same variable id/bid.
I have A and B implementing Comparable.
and my listAll is of type Object?
how do I do it?
thanks.
You could add a common base class and implement comparison there, as in:
abstract class AandBComparable implements Comparable {
public int compareTo(Object o) {
AandBComparable ab = (AandBComparable) o;
return getId().compareTo(ab.getId());
}
public abstract String getId();
}
To be able to sort a list, its elements must be comparable to each other. That's not the case here. Instances of A can only be compared with other instances of A. Same for B.
If you want to sort a list containg A and B instances, you need to provide Comparator which will happily take two As, two Bs or an A and a B, and compare these objects as you want them compared.
public class AOrBComparator implements Comparator<Object> {
#Override
public int compare(Object o1, Object o2) {
String o1Id = getId(o1);
String o2Id = getId(o2);
return o1Id.compareTo(o2Id);
}
private String getId(Object o) {
if (o instanceof A) {
return ((A) o).getId();
}
else if (o instanceof B) {
return ((B) o).getId();
}
else {
throw new IllegalArgumentException("Can only get ID from A or B");
}
}
}
But maybe A and B should implement the same Identifiable interface, and the list should be a List<Identifiable>. This way, you could easily write a comparator that compares two instances of Identifiable, and it would work whether the actual instance is A, B, or any other Identifiable.
I do not believe the exception raises where you tell, but rather in a call to sort()
Anyway, it means something like
"The sort method expects a List of elements that implement comparable. But you tell him that you are giving a list of Object, and not all Object implement Comparable. So, the compiler cannot be sure that at realtime the objects passed in the list will implement Comparable (as required), and throws an error"
The solution? Define the list with a bound class that implements Comparable
List<A> listAll = new ArrayList<A>();
Update: To have all items in the same list then either:
a) Have all items derive from a common class / interface that implements Comparable. Usually this would be the most Object Oriented Programming friendly approach, since if you want to compare both classes they must be somehow related. Either extend B from A, A from B, A and B from another class, or make A and B implement another interface (which itself implements Comparable).
b) As JBNizet said, use a Comparator.
Again, I strongly recommend using the a solution.
You need a base (dummy) class which will implement Comparable and let A and B derive from class. Something like:
public abstract class MyComparableClass implements Comparable {
}
public class A extends MyComparableClass {
... // implementation of compareTo
}
public class B extends MyComparableClass {
...// implementation of compareTo
}
In your main program then simply define the Collection to be:
List<MyComparableClass> listAll = new ArrayList<MyComparableClass>();