Sorting a list of non-comparable elements - java

Today I was asked this interview question:
If I have a Person class with name, age and salary fields, and I put 100 new instances of this Person in an ArrayList, and then do Collections.sort(list), then on what parameter will the list be sorted?
I understand that I need to have the Person class implement Comparable and then override compareTo, but if I don't do that, what will happen?

It wouldn't compile: the 1-argument version of Collections.sort expects a list of Comparables. Specifically, a List<T> where T implements Comparable<? super T>.

Yes you can sort a collection without making the elements implement the Comparable Interface, you do this
List<YourType> theList = new ArrayList<>();
Collections.sort(theList, new Comparator<YourType>(){
public int compare(YourType obj1, YourType obj2) {
// this method should return < 0, 0 or > 0
// whether obj1 is less than, equal to
// or greather than obj2
return 0;
}
});
/edit,
if you use Collections.sort(List) then it will compile only if the list is generic and it's elements implements Comparable. And if so, then the implementation of the compareTo(Obj) on each element is what will determine the ordering in the list after the sort(List) method is called

as the Collections API states:
public static <T extends Comparable<? super T>> void sort(List<T> list)
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).

If the Person class does not implement Comparable<Person>, then (as the compiler will inform you):
Bound mismatch: The generic method sort(List<T>) of type Collections is not applicable for the arguments (List<Person>). The inferred type Person is not a valid substitute for the bounded parameter <T extends Comparable<? super T>>.
(If you happened to have a Comparator<Person> lying around, Collections.sort(myList, myComparator) would sort it by the ordering specified by the comparator.)

Related

How to sort the Object containing list in asc order

i want to get the data from list in asc order..
List<Region> region = (List<Region>) model.get("region");
if i am using Collection.sort(region);
then showing this,
The method sort(List) in the type Collections is not applicable for the arguments (List)
You need to define the class Region as Comparable or you need to pass an additional parameter of type Comparator to the sort method.
Without a Comparator the signature is
public static <T extends Comparable<? super T>> void sort(List<T> list)[1]
and the javadoc explicitly said that the class must implements Comparable:
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).
in this case you need to modify your code as follow:
public class Region implements Comparable {
public int compareTo(Region o) {
// Add the logic to compare regions here.
}
...
}
The second version with a Comparator has the following signature:
public static <T> void sort(List<T> list, Comparator<? super T> c)
and all the elements must be comparable using the passed Comparator:
Sorts the specified list according to the order induced by the specified comparator. All elements in the list must be mutually comparable using the specified comparator (that is, c.compare(e1, e2) must not throw a ClassCastException for any elements e1 and e2 in the list).
In this case you need to change your code directly calling the sort method as follow:
Collections.sort(regionList, (Region r1, Region r2)-> {
// Add compare logic here
});
Note: there is an additional little mistake in your code the class to use is Collections (with an s) and not the interface Collection. I also changed the name of the list from region to regionList (eventually name it regions) because it is not only a single element but it is a list.
Custom comparator in a line for your code,
List<Region> region = (List<Region>) model.get("region");
Collections.sort(region, new Comparator<Region>() {
#Override
public int compare(Region a1, Region a2) {
return a1.getType().compareToIgnoreCase(a2.getType()); //You can alter this condition based on your condition to compare
}
});
You can leave the sorting to the database query.
As it seems the values should be unique, you could also have as model a SortedSet, implementing class: TreeSet.
i done with it...
Collections.sort(region, new Comparator<Region>() {
public int compare(Region obj1, Region obj2) {
return obj1.getStrRegion().compareTo(obj2.getStrRegion());
}
});

Java Sort with Comparable

I have an ArrayList of Person objects. A Person has name, age and height. My goal is to sort this ArrayList<Person>. I have implemented Comparable<Person> and have defined compareTo() but when I try to sort it, it give me this error:
The method sort(Comparator) in the type ArrayList is not applicable for the argument ()"
The way I understand is that if you implement Comparable and then define compareTo everything else is magically done for you.
Can some one explain how to this works and why I am getting this error?
My guess is that your code looks like this:
ArrayList<Person> people = ...;
people.sort();
Look at the JavaDoc for ArrayList. Do you see a method public void sort() (with no parameters)? No - there is no such method.
That is the meaning of the error: The method sort(Comparator) in the type ArrayList is not applicable for the argument () -- There is a method sort(Comparator), but you have not supplied parameters that match it.
Assuming Person implements Comparable (and therefore has a compareTo() method), you can use Collections.sort(), which sorts arbitrary List<Comparable>
Collections.sort(people);
This is because Collections has a static method:
static <T extends Comparable<? super T>> void sort(List<T> list);
(it also has a sort(List<T> list, Comparator<T> comparator))
... or you can pass a comparator to List.sort(), which is quite easy with Java 8 lambdas:
people.sort((a,b) -> a.compareTo(b));
(Or, if you prefer the old style):
people.sort(new Comparator<String>() {
#Override
public int compare(String a, String b) {
return a.compareTo(b);
}
});
(Actually as of Java 8, this comparator is provided by the standard library, as Comparator.naturalOrder())
The point of comparators is that you can sort according to different criteria. For example:
people.sort((a,b) -> a.lastName().compareTo(b.lastName()));
people.sort((a,b) -> a.lastName().compareToIgnoreCase(b.lastName()));
people.sort((a,b) -> Integer.compare(a.age(),b.age()));
// etc.
... or using methods in Comparator:
people.sort(Comparator.comparing(Person::lastName));
people.sort(Comparator.comparing(Person::lastName)
.thenComparing(Person::firstName));
Either you use a structure which uses the Comparable interface to order its elements when you add a new element inside it :
TreeSet<Person> persons = new TreeSet<>();
Person personOne = ...
Person personTwo = ...
persons.add(personOne);
persons.add(personTwo);
Either you use a List and the Collections.sort(List<T> list) method which takes as argument the list you want to sort (there is an overload of this method but it is not relevant in your case):
List<Person> persons = new ArrayList<>();
Person personOne = ...
Person personTwo = ...
persons.add(personOne);
persons.add(personTwo);
Collections.sort(persons);
With the TreeSet, the elements are sorted as soon as added and with the List, the elements are not sorted when you add them. Only, the call to the Collections.sort() method sorts the list.

Sort array based of specific element

So I have a list of objects, specifically a list of vehicles, that contains cars, motos and bicicles. Each one of this objects has different atributes (car and moto have max velocity while bicicle doesn´t for instance.)
Moto Bicicle and Car are subclasses of Vehicle and Vehicle implements the interface Comparable.
They do have on thing in common, the year when they were made and that's how I wanted to sort my array, using that specific attribute. By default java is sorting based on another attribute the objects have in common which is "color".
This is the code used to sort the arrays.(It has to be this one)
public static <T> void sortArray(Comparable<T>[] a) {
Arrays.sort(a);
}
Is there a way to select the attribute which I would like to use to sort the list?
Thanks for the help in advance.
By default Arrays.sort sorts according to the Comparable natural ordering of its elements. If you want to change it, use Arrays.sort(T[] a, Comparator<? super T> c) instead, it allows to define your own comparator.
http://docs.oracle.com/javase/6/docs/api/java/util/Arrays.html#sort(T[], java.util.Comparator)
You can implement an own comparator and pass it to Arrays.sort(array, comparator). Using lambdas or method handles this is easy:
Vehicle[] v = ...
Arrays.sort(v, (v1,v2) -> v1.getYear() - v2.getYear());
or
Arrays.sort(v, Comparator.comparing(Vehicle::getYear));
Based on that you can write a generic function which sorts an array by a specific attribute:
public static <T, U extends Comparable<? super U>> void sortArray(T[] a,
Function<? super T, ? extends U> attribute) {
Arrays.sort(a, Comparator.comparing(attribute));
}
and use it with
sortArray(v, Vehicle::getYear);

Sorting Object ArrayList using Collections.sort

I have added some Integers to an ArrayList of object type, and want it to be sorted. My code looks like:
List<Object> list = new ArrayList<Object>();
list.add(24);
list.add(2);
list.add(4);
Collections.sort(list); // getting error here
System.out.println("Sorted list ");
for (Object e : list) {
System.out.println(e);
}
I got the following compile-time error:
error : should implement java.lang.Compareble <? super java.lang.Object>
How should I resolve this issue?
Object class doesn't implement Comparable interface. If you're sure you're adding Integer you can use code as below and then perform sorting.
List<Integer> list = new ArrayList<Integer>();
From sort() method docs
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).
The error message that your IDE generating is
The inferred type Object is not a valid substitute for the bounded parameter >
Which means that the Objects being put in that List must implement Comparable interface to accept in sort() method.
Object class not implementing comparable interface, hence the error which you are seeing.
Using Object of type in Generic is not advisable and use specific type. Since you are adding all integers to the list just change your declaration to
List<Object> intList = new ArrayList<Object>();
If any other object of your own type, just implement comparable interface in that class or pass a custom comparator as a second parameter to sort.
Since you have declared your list to have the type List<Object>, you are able to store anything into it, be it comparable or not.
The generic method Collections.sort(List) has a type signature which requires that your list has an element type which implement the Comparable interface, which ensures that all elements can be compared to each other, and it tells the sort method how to compare these elements, as said interface contains the method which can be called to compared two elements. In other words, it does not accept a List that could contain anything.
So is your case, you should change the declaration to
List<Integer> list = new ArrayList<>();
as you are only adding Integer objects. Integer is a type which implements Comparable as integer values have a natural order.
Note that you can simplify your code:
List<Integer> list = Arrays.asList(24, 2, 4);
Collections.sort(list);
System.out.println("Sorted list "+list);
The list returned by Arrays.asList does not support changing its size, but reordering the elements is supported, hence you can sort that list.
As a side note, in the rare case, you have a List<Object> whose type you can’t change, but you know for sure that it contains only elements being naturally comparable to each other, you can circumvent the type constraint of Collection.sort:
Collections.sort(list, null);
The method Collections.sort(List, Comparator) supports arbitrary element types as the second parameter tells how to compare them. As a special case, a comparator of null mandates natural order, but null passes every type check. But, of course, using this trick will backfire when the assumption about the elements is wrong.
Generally, you should ensure that the element type as declared at compile-type is appropriate for the desired operation. Here, using List<Integer> when the list is supposed to contain Integers is the right way.
Instead of doing Collections.sort(list), you can loop through the array and sort the objects from least to greatest.
you can do it like this:
for(int i = 0; i < intList.size(); i++) {
// if an integer is larger than any of the ones after it in the array, switch them.
}
the Object class does not implement the Comperable interface, hence it gives you this error. You should rather define it as List<Integer>, or define a custom comperator class and pass it as an aditional Argument.
public class Comp<T> implements Comparator<T>{
#Override
public int compare(T o1, T o2) {
if(o1 instanceof Integer && o2 instanceof Integer) {
int a = (Integer) o1;
int b = (Integer) o2;
return a-b;
}
return 0;
}
}
// Call it as
Collections.sort(list, new Comp<Object>());
But you may run in several Problems while using a List of Objects and a custom Comperator, since you could add everyting to this list.

How will sort method do a sort, on a list that contains objects of type Foo?

How will a LinkedList of type :
LinkedList<FooClass> list = new LinkedList<FooClass>();
// FooClass extends Comparable
list.add(foo_obj_1);
list.add(foo_obj_2);
list.add(foo_obj_3);
list.add(foo_obj_4);
be sorted by :
Collection.sort(list);
What effect will sort method have on list that contains a list of objects.On what basis will the list be sorted ?
If you make you an object comparable, then you override the compareTo method. You can then write your own custom criteria based on what fields you want to compare. A sort can then be performed as usual.
class Author implements Comparable<Author>{
String firstName;
String lastName;
#Override
public int compareTo(Author other){
int last = this.lastName.compareTo(other.lastName);
return last == 0 ? this.firstName.compareTo(other.firstName) : last;
}
}
If your object does not implement Comparable then you will not be able to perform any of the various sort implementations.
Check out the API of Collections#sort(List<T>). It will only compile if T extends Comparable.
If Foo does not extend Comparable, your code will not compile.
If it does, the list will be sorted by Foo's natural ordering, as explained here.

Categories