Sort array based of specific element - java

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

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.

Java: how to partition a collection into equivalence classes?

I have a list(!) of items:
A
B
C
D
E
...
and I want to group them:
[A, C, D]
[B, E]
...
Groups are defined by:
all items in the group are equal according to a custom function f(a, b) -> boolean
f(a, b) = f(b, a)
Question: is there ready API to do so?
<T> List<List<T>> group(Collection<T> collection, BiFunction<T, T, Boolean> eqF);
UPDATE. This question is totally not for a scenario when you can define some quality to group by! In this case Java 8 Collectors.groupingBy is the simplest answer.
I am working with multidimensional vectors and equality function looks like:
metrics(a, b) < threshold
For this case defining a hash is equal to solving the initial task :)
Your scenario sounds like a good use case for the groupingBy collector. Normally, instead of supplying an equality function, you supply a function that extracts a qualifier. The elements are then mapped to these qualifiers in lists.
i.e.
Map<Qualifier, List<T>> map = list.stream()
.collect(Collectors.groupingBy(T::getQualifier));
Collection<List<T>> result = map.values();
In the case the identity of T is your qualifier, you could use Function.identity() as an argument.
But this becomes a problem when your qualifier is more than 1 field of T. You could use a tuple type, to create an alternate identity for T but this only goes so far, as there needs to be a separate tuple class for each number of fields.
If you want to use groupingBy you really need to create a temperate alternate identity for T, so you don't have to change T's equals and hashCode methods.
To create a proper identity, you need to implement equals and hashCode (or always return 0 for a hash code, with performance downsides). There is no API class for this, that I know of, but I have made a simple implementation:
interface AlternateIdentity<T> {
public static <T> Function<T, AlternateIdentity<T>> mapper(
BiPredicate<? super T, Object> equality, ToIntFunction<? super T> hasher) {
return t -> new AlternateIdentity<T>() {
#Override
public boolean equals(Object other) {
return equality.test(t, other);
}
#Override
public int hashCode() {
return hasher.applyAsInt(t);
}
};
}
}
Which you could use like:
Collection<List<T>> result
= list.stream()
.collect(Collectors.groupingBy(
AlternateIdentity.mapper(eqF, hashF)
))
.values();
Where eqF is your function, and hashF is a hash code function that hashes the same fields as eqF tests. (Again, you could also just return 0 in hashF, but having a proper implementation would speed things up.)
You can use hashing to do this in linear time.
To do this, you need to first implement the hashCode() function in your object, so it returns an equal hash value for equal elements (for example by XOR-ing the hash codes of its instance properties). Then you can use a hash table of sets to group your elements.
Map<Integer, Set<T>> hashMap = new HashMap<>();
for (T element : collection) {
if (!hashMap.containsKey(element.hashCode())
hashMap.put(element.hashCode(), new HashSet<T>());
hashMap.get(element.hashCode()).add(element);
}
As equal elements produce the same hash, they will be inserted into the same equivalence class.
Now, you can obtain a collection of all equivalence classes (as sets) by using hashMap.values();
I'm pretty sure there's nothing in the standard API for this. You might try a third-party collection class, like Trove's TCustomHashSet. (It's interesting that, according to a comment in this related thread, the Guava group has (for now) rejected a similar class. See the discussion here.)
The alternative is to roll your own solution. If you don't have too many items, I'd suggest a brute-force approach: keep a list of item lists and, for each new item, go through the list of lists and see if it is equal to the first element of the list. If so, add the new item to the matching list and, if not, add a new list to the list of lists with that item as the only member. The computation complexity is not very good, which is why I would only recommend this where the number of items is small or execution time performance is not an issue at all.
A second approach is to modify your item class to implement the custom equality function. But to use that with the hash-based collection classes, you'll need to override hashcode() as well. (If you don't use a hash-based collection, you might as well go with the brute force approach.) If you don't want to (or can't) modify the item class (e.g., you want to use various equality tests), I'd suggest creating a wrapper class that can be parameterized with the equality (and hash code) strategy to use. (This is kind of half way between modifying your item class and using the Trove class.)
Here's a simple example grouping strings. You'll need to supply a different function other than identity() if your objects you want to group are more complex.
public class StreamGroupingBy
{
public static void main( String[] args )
{
List<String> items = Arrays.asList(
"a", "b", "c", "d",
"a", "b", "c",
"a", "b",
"a", "x" );
Map<String,List<String>> result = items.stream().collect(
Collectors.groupingBy( Function.identity() ) );
System.out.println( result );
}
}
Output:
{a=[a, a, a, a], b=[b, b, b], c=[c, c], d=[d], x=[x]}
I would also recommend to implement a hashing mechanism. You can do something similar with Guava FluentIterable:
FluentIterable.from(collection)
.index(new Function<T, K>() {
K apply(T input) {
//transform T to K hash
}
})//that would return ImmutableListMultimap<K, T>
.asMap()//that would return Map<K, Collection<T>>
.values();//Collection<Collection<T>>

why the code below wouldn't work unless the array is Integer[], not int[]

Could someone please kindly tell me why the code below wouldn't work unless the array is Integer[], not int[]
Integer[] one = {2,5,8,1,3,4,9};
Arrays.parallelSort(one, (p1, p2) -> p2 - p1);
System.out.println(Arrays.toString(one));
Because Arrays has no method parallelSort that takes an int[] and a Comparator.
The only parallelSort overload that takes a Comparator second argument is the one with a generic first argument (static <T> void parallelSort(T[] a, Comparator<? super T> cmp)), thus requiring that the first argument be an array of references. Integer[] satisfies that requirement, while int[] does not.
Because the signature of parallelSort is: public static <T extends Comparable<? super T>> void parallelSort(T[] a) where T is a class of obhects to be sorted.

Sorting a list of non-comparable elements

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.)

Categories