Consider this code:
import java.util.*;
class jm45 implements Comparator<jm45>
{
private int x;
jm45(int input) { x = input; }
public static void main( String args[] )
{
List list = new ArrayList();
list.add(new jm45(2));
list.add(new jm45(2));
Collections.sort(list); //faulty line
}
public int compare( jm45 t1 , jm45 t2 )
{
return t1.x - t2.x;
}
}
Your class implements Comparator<jm45> instead of Comparable<jm45>.
A Comparator knows how to compare two objects - a Comparable knows how to compare another with itself.
You either need to pass in a comparator for sort() to use (as the second argument) or the values have to be comparable.
Here's a version which uses the Comparable interface instead:
import java.util.*;
class Test implements Comparable<Test>
{
private int x;
Test(int input)
{
x = input;
}
public static void main(String args[])
{
List<Test> list = new ArrayList<Test>();
list.add(new Test(2));
list.add(new Test(2));
Collections.sort(list);
}
public int compareTo(Test other)
{
return x - other.x;
}
}
And here's a version which uses the Comparator interface:
import java.util.*;
class TestComparator implements Comparator<Test>
{
public int compare(Test t1, Test t2)
{
return t1.getX() - t2.getX();
}
}
class Test
{
private int x;
Test(int input)
{
x = input;
}
int getX()
{
return x;
}
public static void main(String args[])
{
List<Test> list = new ArrayList<Test>();
list.add(new Test(2));
list.add(new Test(2));
Collections.sort(list, new TestComparator());
}
}
There's nothing to stop a class implementing Comparator<T> for itself, but it's a little strange for it to do so. For instance, you wouldn't normally ask one string to compare two other ones with each other - it's got nothing to do with the original string itself.
From the Collections.sort javaDoc:
Sorts the specified list into ascending order, according to the
natural ordering of its elements. All elements in the list must
implement the Comparable interface. Furthermore, all elements
in the list must be mutually comparable (that is,
e1.compareTo(e2) must not throw a ClassCastException
for any elements e1 and e2 in the list).
Your class implements Comparator, not Comparable.
Related
I'm trying to create a genetic ArrayList of my class Team but I can't cast Comparable to T despite that T extends Comparable
(I tried extends Comparable without putting < T > and same problem is happening)
public class ArrayList<T extends Comparable>
{
static int MaxSize = 1003;//some random maximum size for our array lists
public int Size = 0;
public int Capacity = 0;
T[] MyList;
public ArrayList()//Default Constructor
{
this(MaxSize);
}
public ArrayList(int Capacity)
{
MyList = (T[]) new Comparable[Capacity]; // Casting
}
}
public class Team implements Comparable<Team>
{
public String Name;
public int Points;
public int GamesCount;
public int Wins;
public int Loses;
public int GoalDifference;//Goals Scored - Goals Against
public int GoalsScored;
public int GoalsAgainst;
public Team(String s)
{
Name = s;
Points = 0;
GamesCount = 0;
Wins = Loses = 0;
GoalDifference = GoalsAgainst = GoalsScored = 0;
}
}
public class Test
{
public static void main(String args[])
{
ArrayList<Team> Teams = new ArrayList<Team>(10);
for(int i = 0 ;i < 10;i++)
{
String TeamName = in.next();
Teams.MyList[i] = new Team(TeamName);//exception here
}
}
}
I am getting the following exception. Many thanks in advance for your help.
Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Comparable; cannot be cast to [LTeam;
at Test.main(Test.java:21)
That's obvious, just think about it for a second.
new Comparable[Capacity];
Here you're creating an array which will contain Comparable elements.
Then you're trying to downcast it to an array of Team, which means trying to use a more specific type, and which is not allowed (Upcasting and downcasting in Java), and as you see, throws a ClassCastException.
As your array will contain only types which extends Comparable, you can have a Comparable array inside your ArrayList implementation.
Comparable[] MyList;
Then you can initialize it without casting
MyList = new Comparable[Capacity];
And remember to implement the Comparable#compareTo method in your Team class, otherwise the code won't compile.
You asked for an example.
public class ArrayList<T extends Comparable> {
private static final int MAX_SIZE = 1000;
private final Comparable<T>[] list;
public ArrayList() {
this(MAX_SIZE);
}
public ArrayList(int capacity) {
list = new Comparable[capacity]; // Casting
}
public T get(final int index) {
return (T) list[index];
}
public void set(final int index, final T value) {
list[index] = value;
}
}
Usage is pretty simple
final ArrayList<Team> teamArrayList = new ArrayList<>(3);
teamArrayList.set(0, new Team("One"));
teamArrayList.set(1, new Team("Two"));
teamArrayList.set(2, new Team("Three"));
final String name = teamArrayList.get(0).Name;
As you extended the Comparable<T> interface, you need to implement it
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
A basic example is
#Override
public int compareTo(final Team other) {
return name.compareTo(other.name);
}
In your code, T is Team.
Team IS-A Comparable. Hence, you can cast Team to Comparable.
But every Comparable is not a Team. Therefore, Comparable cannot be cast to Team.
watch this statement: MyList = (T[]) new Comparable[Capacity]; it initializes an array from Comparable interface and put it in to MyList field with type of Team[].
you can test it with:
{
MyList = (T[]) new Comparable[Capacity]; // Casting
System.out.println( MyList.getClass().toString());
}
it writes class [Ljava.lang.Comparable; in output... so in the first attempt to access this field from outside of class you will get ClassCastException even by calling length in test method like this:
public class Test
{
public static void main(String args[])
{
MArrayList<Team> Teams = new MArrayList<Team>(10);
int length = Teams.MyList.length; // this line throws ClassCastException
....
}
}
To solve your problem I suggest change your ArrayList class like this:
public class ArrayList<T extends Comparable<T>>
{
...
Comparable<T>[] MyList;
...
public ArrayList(int Capacity)
{
MyList = new Comparable[Capacity]; // Casting
}
}
I have an error on the following line:
Collections.sort(l);
Can you explain why?
This is program code:
import java.io.*;
import java.util.*;
public class Esame{
public static void main(String args[]) throws IOException{
ArrayList<Studente> l=new ArrayList<Studente>();
BufferedReader b=new BufferedReader(new InputStreamReader(System.in));
String cognome = b.readLine();
while(!cognome.equals("")){
System.out.println("inserire nome");
String nome = b.readLine();
System.out.println("inserire matricola");
String matricola = b.readLine();
Studente s = new Studente(nome, cognome, matricola);
l.add(s);
System.out.println("inserire cognome");
cognome = b.readLine();
}
b.close();
Collections.sort(l);
Iterator<Studente> i=l.iterator();
PrintStream p=new PrintStream(new File("uscita.txt"));
while(i.hasNext()){
p.println(i.hasNext());
}
p.close();
}
}
class Studente{
protected String nome,cognome;
protected int matricola;
public Studente(String nome,String cognome,String matricola){
this.nome=nome;
this.cognome=cognome;
this.matricola=Integer.parseInt(matricola);
}
public String toString(){
return "\n NOME: "+nome+"Cognome: "+cognome+"MATRICOLA: "+matricola;
}
public int compareTo(Object x) {
Studente p = (Studente) x;
return cognome.compareTo(p.cognome);
}
}
You can just implements Comparable<T> to fix the problem, or you can pass a Comparator to the sort method, like:
Collections.sort(l, new Comparator<Studente>() {
#Override
public int compare(Studente o1, Studente o2) {
// some kind of comparation
return o1.matricola - o2.matricola;
}
});
This way your class doesn't need to implement Comparable interface
class Studente implements Comparable<Studente>{
protected String nome,cognome;
protected int matricola;
public Studente(String nome,String cognome,String matricola){
this.nome=nome;
this.cognome=cognome;
this.matricola=Integer.parseInt(matricola);
}
public String toString(){
return "\n NOME: "+nome+"Cognome: "+cognome+"MATRICOLA: > "+matricola;
}
public int compareTo(Studente x) {
//how to compare
return 0;
}
you have to make you class implement comparable.
Changes
1.) Implement Comparable interface in Student Class
public class Studente implements Comparable<Studente>{
}
2.) Change overridden method compareTo
#Override
public int compareTo(Studente o) {
Studente p = (Studente) o;
return cognome.compareTo(p.cognome);
}
From Javadoc,
Sorts the specified list into ascending order, according to the
natural ordering of its elements. All elements in the list must
implement the Comparable interface. Furthermore, all elements in the
list must be mutually comparable (that is, e1.compareTo(e2) must not
throw a ClassCastException for any elements e1 and e2 in the list).
Refer Collections#sort for more details
your class Studente has to implement the Comparable interface in order to be sorted!
Otherwise .sort(...) does not know how to compare the Studente objects.
Just change your Arraylist to just List.
ArrayList<Studente> l=new ArrayList<Studente>();
Should be
List<Studente> l=new ArrayList<Studente>();
Collections.sortmethod accepts anListand not anArrayList`
See api
Snippet from Spec
Sorts the specified list into ascending order, according to the
natural ordering of its elements. All elements in the list must
implement the Comparable interface. Furthermore, all elements in the
list must be mutually comparable (that is, e1.compareTo(e2) must not
throw a ClassCastException for any elements e1 and e2 in the list).
Your Studente class should implement Comparable interface for your sort to work correctly.
Implement Comparable interface in Studente class
class Studente implements Comparable<Studente>{
protected String nome,cognome;
protected int matricola;
public Studente(String nome,String cognome,String matricola){
this.nome=nome;
this.cognome=cognome;
this.matricola=Integer.parseInt(matricola);
}
public String toString(){
return "\n NOME: "+nome+"Cognome: "+cognome+"MATRICOLA: "+matricola;
}
#Override
public int compareTo(Studente o) {
// Logic for compare has to go here
return 0;
}
I am having trouble trying to sort the following list.
Simply using Collections.sort(mylist); is showing an error which I am unable to understand as I'm a beginner to java
Myclass x1 = new Myclass("8", "12");
Myclass x2 = new Myclass("6", "9");
Myclass x3 = new Myclass("11", "14");
List<Myclass> mylist = Arrays.asList(x1, x2, x3);
How to sort "mylist" such that it is stored in the following order-(6,9),(8,12),(11,14)
i.e according to the first value of the tuples
Do you really want to pass the numbers as strings, but then compare them as numbers if possible?
I assume Myclass has the following interface:
class Myclass {
public Myclass(String first, String second) {..}
public String getFirst() {..}
public String getSecond() {..}
}
then implement your custom Comparator as follows. Since you are comparing strings but considering numbers, use the AlphanumComparator (place the java implementation into some package):
public class MyComparator implements Comparator<Myclass> {
private final static AlphanumComparator alphaNum = new AlphanumComparator();
#Override
public int compare(Myclass a, Myclass b) {
return alphaNum.compare( a.getFirst(), b.getFirst() );
}
}
and you can call:
Collections.sort(myList, new MyComparator())
Or you can have your class implement the Comparable interface and then Collections.sort(myList) would sort it according the compareTo method of that interface.
However, if your question is not asked correctly, and Myclass is actually a pair of integers (not strings), then you can simply define your comparator as follows:
public class MyComparator implements Comparator<Myclass> {
#Override
public int compare(Myclass a, Myclass b) {
return a.getFirst() - b.getFirst();
}
}
Collections.sort(myList, myComparator);
myComparator should be your custom implementation of the Comparator interface.
Assuming that Myclass has a getFirstValue method your Comparator can be:
public class MyComaparator implements Comparator<Myclass> {
#Override
public int compare(Myclass arg0, Myclass arg1) {
return arg0.getFirstValue() - arg1.getFirstValue();
}
}
Please note that I also assumed that getFirstValue() does not return null. The validation part is up to you. I suggest you should use primitives in your Tuple implementation to solve this problem easily.
The official docs is here.
Because your object don't have natural order (like integers), you need to use a specify a comparator mechanism, yourself.
Collections.sort(myList, new Comparator<Myclass>() {
public int compare(Myclass o1, Myclass o2) {
// I don't know how you access the first integer in your 'MyClass'
// replace by your own.
int number1 = o1.getFirstElement();
int number2 = o2.getFirstElement();
if (number1 < number2 ) {
return -1;
} else if (number1 > number2 ) {
return 1;
} else {
return 0;
}
}
});
You always compare two objects, so you can only have -1 1 and 0 as result, even if the difference between two objects is bigger:
Comparing (6,9) and (8,12) will result the comparator returning 1 when the difference is 2
Comparing (8,12) and (11,14 will result the comparator returning 1 when the difference is 3
And the sorting operation will still be correct.
The rules are:
sgn(compare(x,y)) = -sgn(compare(y,x))
if compare(x,y) > 0 and compare(y, z) -> compare(x,z) > 0
if comapre(x,z) = 0 -> sgn(compare(x,y)) = -sgn(compare(y,x))
You can achieve this by using the comparable or the comparator interface. As yours is a custom object you have to tell the Collections.sort method as to what basis it should compare two objects in the collection.
Here is a good example on how to do it.
Two methods you can use to implement sort function with your class Myclass.
Method#1 --> Use Myclass to implement Comparable, and write custom comparison in compareTo method. Like,
Myclass ==>
public class Myclass implements Comparable {
private String value1;
private String value2;
public Myclass(String value1, String value2) {
super();
this.value1 = value1;
this.value2 = value2;
}
public String getValue1() {
return value1;
}
public void setValue1(String value1) {
this.value1 = value1;
}
public String getValue2() {
return value2;
}
public void setValue2(String value2) {
this.value2 = value2;
}
#Override
public int compareTo(Object o) {
Myclass cl = (Myclass)o;
return Integer.valueOf(value1) - Integer.valueOf(cl.getValue1()) ;
}
}
Test code==>
public class Main {
public static void main(String[] args) {
Myclass x1 = new Myclass("8", "12");
Myclass x2 = new Myclass("6", "9");
Myclass x3 = new Myclass("11", "14");
List<Myclass> mylist = Arrays.asList(x1, x2, x3);
//Sort list
Collections.sort(mylist);
//Print List content
for( Myclass cla : mylist )
{
System.out.printf("(%s,%s)", cla.getValue1(),cla.getValue2());
}
}
}
Output in Console==>
(6,9)(8,12)(11,14)
Use this method, you can use Collections.sort(myList); to sort the object in list.
Method#2 -->
Write a customer Comparator if you do not want make Myclass implement Comparable.
Use this method, you can use Collections.sort(myList,customComparator); to sort the object in list.
Regarding Method#2, Adam has provided example above. I am not going to provide it here.
I have a range of objects which needs to be ordered with a rule. But I need to be able to switch the rules however I have a limited set of ordering rules. Which data structure would be the best choice for that?
As an example I have this class:
class Test {
public final int amount;
public final int cost;
public final String name;
public final int whatever;
// ...
// TODO: add a constructor to set the fields :-)
}
How can I store those fields to order them by amount, cost, name or whatever. But just one of that rules.
I could imagine to use an ArrayList or a HashSet where I call the sort function with a custom Comparator. But I cannot imagine that this is efficiency. I think this is important on a mobile device. What is a better way to achieve that?
You can not use an Set for sorting, as it does not have any order. How every the concept of having List and custom Comparator<T> is reasonable.
You should go with that solution and do not care about performance at this point. If you will be not satisfied from gained result then try to came up with better solution.
The best solution is reading data from storage in proper order. I do not know how your app store that structure. Therefore I can not help you with that. But implement the comparable solution and you will see that is not so bad.
What is important on mobile device is memory usage. If your application will use lot of those sorting operation you could create the Comparators as enums so they will be loaded only once and in addition can simplify the code
private enum TestComparator implements Comparator<Test> {
BY_NAME {
#Override
public int compare(Test o1, Test o2) {
//We validate first against null
return o1n.name.compareTo(o2.name);
}
}
BY_WHATEVER{
#Override
public int compare(Test o1, Test o2) {
//We validate first against null
return (o1.whatever<o2.whatever ? -1 : (o1.whatever==o2.whatever ? 0 : 1));
}
}
}
Do this:
class Test {
public final int amount;
public final int cost;
public final String name;
public final int whatever;
// ...
// TODO: add a constructor to set the fields :-)
class TestAmountComparator implements Comparator<Test> {
#Override
public int compare(Test t1, Test t2) {
return Integer.valueOf(t1.amount).compareTo(Integer.valueOf(t2.amount))
}
}
class TestCostComparator implements Comparator<Test> {
#Override
public int compare(Test t1, Test t2) {
return Integer.valueOf(t1.cost).compareTo(Integer.valueOf(t2.cost))
}
}
}
Store your Test objects in ArrayList (or any other Collection) and then sort them this way:
List<Test> list = new ArrayList<Test>(myTest); //your Test list
//sorting
Collections.sort(list, new TestAmountComparator()); //sort by amount
Collections.sort(list, new TestCostComparator()); //sort by cost
I would have preferred implementing Comparable interface and implement compareTo method in the class. It provides consistence behaviour of sorting across different data structures.
In this case Comparator interface will be used only if special sorting is required.
class Test implements Comparable {
public final int amount;
public final int cost;
public final String name;
public final int whatever;
// ...
//add equals ,hashcode, and compareTo method in the class...
// TODO: add a constructor to set the fields :-)
}
You can use TreeSet if instances are unique and comparable is implemented. Else otherwise you have to use lists and order them with Collection.sort function.
You can decide the DS based on access use. If you want to access the elements in sequence use LinkedList else user ArrayList
My version:
class Test3 implements Comparable<Test3> {
public int amount;
//...
Comparator<Test3> comparator;
public void setComparator(Comparator<Test3> comparator) {
this.comparator = comparator;
}
#Override
public int compareTo(Test3 o) {
return comparator.compare(this, o);
}
}
This is how we can use TreeSet without losing duplicates:
import java.util.Comparator;
import java.util.TreeSet;
public class Test {
int amount;
Test(int amount) {
this.amount = amount;
}
public static void main(String args[]) throws Exception {
Comparator<Test> c = new Comparator<Test>() {
#Override
public int compare(Test o1, Test o2) {
if (o1.amount >= o2.amount) {
return 1;
}
return -1;
}
};
TreeSet<Test> s = new TreeSet<Test>(c);
s.add(new Test(2));
s.add(new Test(1));
s.add(new Test(1));
for (Test t : s) {
System.out.println(t.amount);
}
}
}
This prints:
1
1
2
Suppose I have two classes CLassA and CLassB. And they have one atributte in common, for example the number of elements that each class holds.
How can i create a collection from objects of ClassA and CLassB and sort by that attribute (ascending of descending order, doesn't matter)?
I made a collection of type but when I try to implement the Comparable Interface i can't acess to that method (a get that returns the nr of elements for example).
What solutions do I have?
Thanks for your help!
You could make a custom java.util.Comparator and sort using the Collections.sort(List list,
Comparator c) method.
Really ClassA and ClassB should be related either through an inheritance hierarchy, or by a common interface if you are going to put them both in the same collection.
The simplest thing would be to have a common interface that provides an accessor method for the common attribute. And then the comparator could use that method (through the interface) for fetching the value from ClassA's instance as well as ClassB's instance.
Hmm.. is it possible for ClassA and ClassB to share an interface?
interface InterfaceZ
{
int getCount();
}
class ClassA implements InterfaceZ
{
int getCount() { return _myArray.length; }
}
class ClassB implements InterfaceZ
{
int getCount() { return _complexCollection.size(); }
}
Then just sort the list like so:
List<InterfaceZ> myArray;
... fill up array ...
Collections.sort(myArray, new Comparator<InterfaceZ>() {
public int compare(InterfaceZ o1, InterfaceZ o2) {
return o2.getCount() - o1.getCount();
}});
If you have access to the declaration of CLassA and ~B, then go with a common interface, if not you could write a Wrapper for both Classes:
I defined - against the description - my own classes ~A and ~B, to have something to test. Imagine they're foreign source, and you just have access to the classes.
import java.util.*;
public class SortAB
{
class CLassA {
int [] elements;
public CLassA (int [] a) {elements = a;}
public int getElementCount () {return elements.length;}
}
class CLassB {
List <Integer> elements;
public CLassB (List <Integer> l) {elements = l;}
public int getElementCount () {return elements.size ();}
}
/** a common element-count-wrapper with compareTo method */
abstract class EcWrapper <T> implements Comparable <EcWrapper> {
public abstract int getElementCount ();
public int compareTo (EcWrapper o) {return getElementCount () - o.getElementCount ();}
}
/** concrete Wrapper for CLassA */
class EcAWrapper extends EcWrapper <CLassA> {
private CLassA inner;
public EcAWrapper (CLassA t) {
inner = t;
}
public int getElementCount () {return inner.getElementCount (); }
}
/** concrete Wrapper for CLassB */
class EcBWrapper extends EcWrapper <CLassB> {
private CLassB inner;
public EcBWrapper (CLassB t) {
inner = t;
}
public int getElementCount () {return inner.getElementCount (); }
}
// testing
public SortAB ()
{
int [] ia = {3, 5, 7, 6, 9, 11, 14};
List <Integer> il = new ArrayList <Integer> ();
for (int i: ia)
il.add (i);
il.add (15);
il.add (16);
CLassA a = new CLassA (ia);
CLassB b = new CLassB (il);
List <EcWrapper> list = new ArrayList <EcWrapper> ();
list.add (new EcBWrapper (b));
list.add (new EcAWrapper (a));
show (list);
Collections.sort (list);
show (list);
}
public static void main (String args[])
{
new SortAB ();
}
public static void show (List <EcWrapper> list)
{
for (EcWrapper e: list)
System.out.println ("\t" + e.getElementCount ());
System.out.println ("---");
}
}