Java Sorting Objects with Multiple Parameters - java

I have recently been working to better my understanding of sorting algorithms and their relationship to different types of input. Currently, I'm working on a Student Management program where each student has three parameters: Last Name, GPA, and User ID (String, double, int). They are each stored in a Student class with those three parameters, and there are DOZENS of students (a key feature of the program is to input, remove, and update students).
My question is: using the major sorting algorithms (mergesort, quicksort, etc.), what is the best way to sort my list of students by each parameter? For instance, what is the best way to perform a mergesort to sort the list by GPA? Or to use quicksort to sort the list by last name?
Essentially my question boils down to...I can sort these objects if they didn't have three parameters (writing a mergesort to sort 100 numbers is very easy for me). How do I manage the other two parameters and make sure they're accessible after the sort?

The way this is done in Java is to use different Comparators. Then you say:
Collections.sort(list, new NameComparator());
Or
Collections.sort(list, new GpaComparator());
These comparators use different fields to define the order between two elements.
For example, Name Comparator might be:
class NameComparator implements Comparator< Student> {
#Override public int compare(Student left, Student right) {
return left.getName().compareTo(right.getName());
}
}
And GpaComparator might be
class GpaComparator implements Comparator< Student> {
#Override public int compare(Student left, Student right) {
if (left.getGpa() < right.getGpa()) {
return -1;
} else if (left.getGpa() > right.getGpa()) {
return 1;
} else {
return 0;
}
}

The typical way to do this is to write a generic sorting algorithm on any type that accepts a Comparator, and then to write different Comparators to sort by different fields.

I would recommend implementing the Comparable interface in your Student Class like this
public class Student implements Comparable {
public int compareType; //you can make this an enum if you want
...
public int compareTo(Object o) {
if(compareType == 0)
return gpaCompareTo(o);
else if(compareType == 1)
return nameCompareTo(o);
return idCompateTo(o);
}
public int gpaCompareTo(Object o) {
//implement your gpaCompareTo
}
public int nameCompareTo(Object o) {
//implement your nameCompareTo
}
public int idCompareTo(Object o) {
//implement your idCompareTo
}
}
And then use a built-in sort like
List<Student> list = new ArrayList<Student>();
...
Collections.sort(list);
Or you can not implement Comparable and design your own comparators
public class MyComparator implements Comparator<Student> {
public int compare(Student o1, Student o2) {
//implement the comparator
}
public boolean equals(Object o) {
//implement the equals
}
}
Then you can use the other Collection's sort method
Collections.sort(list, MyComparator);

This is probably off topic, but if you want to try something cool, the JDK 8 Lambda Preview offers a few cool ways to define comparators using Lamda expressions and method references.
Let's say we have a class:
class Jedi {
private final String name;
private final int age;
//...
}
And then a collection of them:
List<Jedi> jediAcademy = asList(new Jedi("Obiwan",80), new Jedi("Anakin", 30));
sort(jediAcademy, (j1, j2) -> j1.getAge() > j2.getAge() ? 1 : j1.getAge() < j2.getAge() ? -1 : 0);
System.out.println(jediAcademy); //Anakin, Obiwan
Or with method references, supposing Jedi has method that behaves as a comparator (same signature)
class Jedi {
public static int compareByAge(Jedi first, Jedi second){
return first.age > second.age ? 1 : first.age < second.age ? -1 : 0;
}
//...
}
Which could be used as follows to generate a comparator by using a method reference:
List<Jedi> jediAcademy = asList(new Jedi("Obiwan",80), new Jedi("Anakin", 30));
sort(jediAcademy, Jedi::compareByAge);
System.out.println(jediAcademy);//Anakin, Obiwan

It's really no different than sorting numbers, except that in this case your "digits" are the three fields of your users, the value of each digit is constrained by the values of each field, and the order of the fields determines the sort ranking.
To be a bit more specific, you have a Tupple with 3 fields: <GPA, Last Name, User ID>, and lets assume that you want to sort by GPA, and then Last Name, and the User ID.
In the same way that 219 is sorted above 139 (ie. the "hundreds" digit has a higher value even though the "tens" digit is lower), a tupple like <3.75, Jones, 5> will be sorted above <3.0, Adams, 2> because the "GPA digit" (which is more significant) has a higher value, even though the "last name digit" is lower (eg. Jones is "lower" than Adams).

Use multiple comparators
class Student
{
String lastName;
dounle GPA;
int userId
static Comparator<Student> getStudentLastNameComparator() {
return new Comparator<Student>() {
#Override
public int compare(Student Student1, Student Student2) {
return Student1.getlastName().compareTo(Student2.getlastName());
}
// compare using Student lastName
};
}
static Comparator<Student> getStudentGPAComparator() {
return new Comparator<Student>() {
#Override
public int compare(Student Student1, Student Student2) {
if(Student1.GPA < Student2.GPA)
return 1;
else
return -1;
}
// compare using Student GPA
};
}
static Comparator<Student> getStudentUserIdComparator() {
return new Comparator<Student>() {
#Override
public int compare(Student Student1, Student Student2) {
if(Student1.userId < Student2.userId)
return 1;
else
return -1;
}
// compare using Student userId
};
}
}

Related

Fastest way to sort an array of objects in java

I have a Class called apple which contains 3 values as int x, int y and int weight. Then i created an array of apple type objects. Now i want to sort the the array of objects based on weight meaning the the apple object with the lowest weight should be first and so on.
I know there are quite a few ways to achieve this by using Arrays.sort etc or comparators.
I was wondering what is the fastest way of doing this sort in Java? There can be a case where i have 500,000 objects so i want to know which sort i should use, more importantly which approach will give me best approach. i have even wrote my own quick sort with Hoare partition.
Code for Apple class
public class Apple {
public int x;
public int y;
public int weight;
public Apple(int a, int b, int w) {
x = a;
y = b;
weight = w;
}
}
Code for main class
public class main {
static Apple[] appleArray;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int size = sc.nextInt();
int totalApples = sc.nextInt();
appleArray = new Edge[totalApples];
int x = 10;
int y = 20;
int w = 30;
for (int i = 0; i < size; i++) {
appleArray[i] = new Apple(x, y, w);
x++;
y++;
w++;
}
//Now i want to sort array of apple objects based on weight
}
}
This book has a useful cheat sheet for deciding the optimal sort for your needs: https://www.safaribooksonline.com/library/view/algorithms-in-a/9780596516246/ch04s09.html
The easiest solution
The Arrays.sort command uses a quicksort implementation, which is suitable for many situations. For your example code this might be:
Arrays.sort(appleArray, new Comparator<Apple>(){
#Override
public int compare(Apple apple1, Apple apple2){
return apple1.weight - apple2.weight;
}
});
The fastest solution
In your case you have a large array containing repetitions, for example 50,000 apples in your array might all weigh 3 ounces... You might therefore opt to implement a bucket sort to improve performance over a quicksort, which can be wasteful under such conditions. Here is an example implementation.
Perhaps benchmark a few researched choices, using the Java API when it suits, to determine the best solution for your input set.
I would first use Java API. If this is not fast enough then I would search for a optimized sorting library.
Also consider a database, DB engines are fast and optimized for sorting large data sets.
You can use Arrays.sort passing a custom Comparator or defining your Apple as Comparable
We can use Collections.sort with a custom Comparator.
class Apple {
public final int weight;
// ...
};
List<Apple> apples = // ...
Collections.sort(apples, new Comparator<Apple>() {
#Override public int compare(Apple a1, Apple a2) {
return a1.weight - a2.weight; // Ascending
}
});
If in your object there is any Number or Integer over which you have to sort you can do like this-
List<Object> obj = new ArrayList<Object>();
Collections.sort(obj, new Comparator<Object>() {
#Override
public int compare(Object object1, Object object2) {
return object1.getNumber() > object2.getNumber() ? 1 : -1;
}
});
And if there is not Number or Integer over which you can sort it and you are just having Strings in your object than assign value to Strings by enum.
enum Code {
Str1(1), Str2(2), Str3(3), Str4(4), Str5(5));
int sortNumber;
Code(int sortNumber) {
this.sortNumber = sortNumber;
}
int returnNumber() {
return sortNumber;
}
};
public static void main(String[] args) {
List<Object> obj = new ArrayList<Object>();
Collections.sort(obj, new Comparator<Object>() {
#Override
public int compare(Object object1, Object object2) {
return Code.valueOf(object1.getStr()).returnNumber() > Code.valueOf(object2.getStr()).returnNumber() ? 1 : -1;
}
});
}
With the help of new lambda expressions which came with Java8, now it's way easier and shorter in code to sort Arrays of custom Objects. Below is the code showing the use of lambda expressions in the current scenario.
Arrays.sort(appleArray, (apple1, apple2) -> apple1.weight - apple2.weight);

Sorting string using collection.sort in java

I am a beginner in Java and I am having a problem sorting String. I know using Collections.sort it will sort String with case-sensitive by default but the uppercase ones always come in the front because it always compares the ASCII values. If I want to keep lowercase String in front of uppercase ones, is there a clean way to implement it in Java using Collections.sort?
For example, to sort "java Python ruby C" into "java ruby C Python".
Thanks in advance.
You should implement your own Comparator to define such an ordering:
Collections.sort(list, new Comparator<String>() {
#Override
public int compare(String s1, String s2) {
if (/* s1 is lower case and s2 is upper case */) {
return 1;
}
if (/* s1 is upper case and s2 is lower case */) {
return -1;
}
return s1.compareTo(s2);
}
});
Of course you can use a Comparator .Use Collections.sort(List<String> x,Comparator comp)
This is achieved by creating a Comparator that will impelemt the compare method.
Here is example of using AgeComparator:
import java.util.Comparator;
public class HeightComparator implements Comparator<Object> {
#Override
public int compare(Object o1, Object o2) {
int p1 = ((Person)o1).height;
int p2 = ((Person)o2).height;
return p1 - p2;
}
}
Now all that left is to pass our custom comparator with the collection we want to compare.
Collections.sort(peoples, new AgeComparator())
More on Comparators and Comparable can be read HERE.
arshajii's answer is solid. Here're the two minor conditional statements:
Collections.sort(list, new Comparator<String>() {
#Override
public int compare(String s1, String s2) {
if (s1.toLowerCase().equals(s1) && s2.toUpperCase().equals(s2)) {
return 1;
}
if (s1.toUpperCase().equals(s1) && s2.toLowerCase().equals(s2)) {
return -1;
}
return s1.compareTo(s2);
}
};

How to correctly initialize a Comparator?

I need to write a static method in a class MinTester that computes the "smallest" string
from an ArrayList collection using a comparator object:
public static String min(ArrayList<String> list, Comparator<String> comp)
I cannot use the Collections class to compute the minimum.
Here is what I have so far.
public class MinTester
{
public static String min(ArrayList<String> list, Comparator<String> comp)
{
String shortest = list.get(0);
for(String str : list) {
if ( comp.compare(str, shortest) < 0) {
shortest = str;
}
}
return shortest;
}
}
I am not getting any errors here from the method, So I try to test it in Main with this.
I get this error when trying to pass comp: Variable comp may not have been initialized
public static void main(String[] args)
{
// TODO code application logic here
MinTester s = new MinTester();
Comparator<String> comp;
ArrayList<String> list = new ArrayList<>();
list.add("a");
list.add("ab");
list.add("abc");
list.add("abcd");
String a = s.min(list,comp);//Error: Variable comp may not have been initialized
System.out.println(a);
}
Heres where I run into my problem.
I try
Comparator<String> comp = new Comparator<>();//Error:Comparator is abstract, cannot be instantiated
Comparator<String> comp = new MinTester();//Error: MinTester cannot be converted to Comparator<String>
Can anyone tell me the proper way to handle this Comparator? Im not sure if Im just trying to initialize it incorrectly, or if I'm missing something in my MinTester class.
You should write a class that implements Comparator<String> for this. A quick approach using anonymous class:
String a = s.min(list, new Comparator<String>() {
#Override
public int compare(String s1, String s2) {
return s1.compareTo(s2);
}
});
Since you need to compare based on String length, just change the comparison logic in the compare method:
String a = s.min(list, new Comparator<String>() {
#Override
public int compare(String s1, String s2) {
return (s1.length() > s2.length()) ? 1 : (s1.length() < s2.length()) ? -1 : 0;
}
});
If you happen to use Java 7, then use Integer#compare:
String a = s.min(list, new Comparator<String>() {
#Override
public int compare(String s1, String s2) {
return Integer.compare(s1.length(), s2.length());
}
});
If you use Java 8, you can use a lambda expression:
String a = s.min(list, (s1, s2) -> Integer.compare(s1.length(), s2.length()));
Comparator is an interface; different classes can implement it in different ways to perform different kinds of comparisons. The reason why your method takes a Comparator is so that the caller can choose how the strings should be compared. Pass in a Comparator that does lexical (aka alphabetical) comparison, and you'll get the first string in lexical order. Pass in a Comparator that looks at string length, and you'll get the shortest string.
Since the String class already implements the Comparable interface — a sort of sibling to Comparator that lets a class define its own comparison method — here's a handy generic class that lets you use any Comparable through the Comparator interface:
public final class ComparableComparator<T extends Comparable<T>> implements Comparator<T> {
#Override
public int compare(final T a, final T b) {
return a.compareTo(b);
}
}
Pass one of those into your method and it'll compare the strings using the String class's own compareTo method.
Edit: In Java 8 or later, the Comparator.naturalOrder() method gives you the same thing, so you don't need to write the class above.
You do not need to use Comparator, at least not unless you want to modify the natural ordering of the string comparisons. Use the compareTo() method for the String class instead.
if (str.compareTo(shortest) < 0) {
shortest = str;
}
If at all you wish to modify the natural ordering, you can create a class which implements the Comparator interface and then pass an instance of this class to the compare() method. You can also define your own logic for the comparisons.
public class StringDescComparator implements Comparator<String> {
#Override
public int compare(String str1, String str2) {
// return str1.compareTo(str2); // For natural ordering
return -1 * str1.compareTo(str2); // For reversed ordering
}
}
Then you can use an instance of the above class to compare in the descending order such that: "b" < "a"
Comparator comp = new StringDescComparator();

Java Comparable issue [duplicate]

This question already has answers here:
sort arraylist of complex objects alphabetically
(2 answers)
Closed 9 years ago.
I want to sort objects by a string they have. Just wondering does this make sense?
Before now I have only used Arrays.sort(BlahList); But now I could have many objects and not just an arraylist of strings.
public class Contact implements Comparable
{
private string name;
public compareTo (Contact Contact1)
{
return this.name.compareTo(Contact1.name);
}
}
and in the main method I have:
Collections.sort(ContactList);
I would also like to know if this would work for integers if the name was age?
Firstly, you should type the Comparable interface:
public class Contact implements Comparable<Contact>
Secondly, you should use leading lowercase for your parameters/variables:
public compareTo (Contact contact)
Thirdly, prefer not using this. unless necessary - it's just code clutter:
return name.compareTo(contact.name);
And finally, yes, you can compare age like this:
return age - contact.age; // order youngest to oldest
Or the cleaner way (thanks for pointing this out JB):
return Integer.compareTo(age, contact.age);
This whole class should look like this:
public class Contact implements Comparable<Contact> {
private string name;
public int compareTo(Contact contact) {
return name.compareTo(contact.name);
}
}
Note: You were missing the return type int from the code for your compareTo() method.
To compare age instead, replace the compareTo() method with this:
public int compareTo(Contact contact) {
return Integer.compareTo(age, contact.age);
}
it works for all. if it is int u need to write following code in compareTo method
return this.age-contact1.age// for ascending order
contact1.age-this.age // for descending order
In java 7 you can use Integer.compare(age, contact.age).
Its (almost) same as (x < y) ? -1 : ((x == y) ? 0 : 1); but much more readable (Integer.compare does not say it will return those exact numbers, but Oracle's implementation will, it could return any other positive instead of 1 and negative instead of -1)
btw. DON't use age-contact.age, beacause Integer.MIN_VALUE-Integer.MAX_VALUE = 1
For complex comparators (eg. first by name then by age if name is equal) I suggest use some library like google guava.
If you want multiple comparators then I would suggest you to use Comparator interface:
For Name compare:
public class NameCompare implements Comparator<Contact> {
#Override
public int compare(Contact a, Contact b) {
if (a.getName().compareToIgnoreCase(b.getName())>0)
return 1;
else if (a.getName().compareToIgnoreCase(b.getName())<0)
return -1;
return 0;
}
}
For Age Compare:
public class AgeCompare implements Comparator<Contact> {
#Override
public int compare(Contact a, Contact b) {
if (a.getAge() > b.getAge())
return 1;
else if (a.getAge() < b.getAge())
return -1;
return 0;
}
}
And in the main, you just pass the desired Comparator:
ArrayList al = new ArrayList<Contact>
Collections.sort(al, new NameCompare())
Collections.sort(al, new AgeCompare())

how to order set of objects based on array of integers

I have a class called Variable
Class Variable{ private String name; private int[] domain; //...etc}
which represents variable in specific structure (constraint satisfaction problem).
I have instantiated set of variables in ArrayList< Variable > and filled up an array of integers.
ArrayList<Variable> vars=new ArrayList<Variable>();
Variable a=new Variable("A",new int[]{1,2});
vars.add(a);
// Define all variables;
int[] cons=new int[vars.size()];
for(int i=0;i<cons.length;i++)
cons[i]=number_of_constraints(vars.get(i));
// cons contains number of involved constraints for each variable
Now I need to sort them descending based on the number of constraints.
In other words: Given list of Objects [(A,{1,2}) , (B,{3,4}) , (C,{5,6}) ] and an array of integers cons={1,2,0} how to sort the list of objects descending based on the array of integers?
Use a sorted collection like a TreeSet
class Variable {
private String name;
private int[] domain;
};
final Set<Variable> variables = new TreeSet<Variable>( new Comparator<Variable>() {
public int compare(Variable o1, Variable o2) {
//Do comparison here
//return -1 if o1 is less than o2
//1 if o1 is greater than o2
//0 if they are the same
}
});
Now you have a sorted Set of your Variables. This is guaranteed to always be sorted.
If you would like to keep Class Variable intact, the following code will sort the given vars outside:
Collections.sort(vars, new Comparator<Variable>() {
public int compare(Variable var1, Variable var2) {
return var2.number_of_constraints() - var1.number_of_constraints();
}});
If you can change Class Variable, let it implement interface Comparable:
class Variable implements Comparable<Variable> {
//...
public int compareTo(Variable other) {
return this.number_of_constraints() -
other.number_of_constraints();
}
}
Then you can sort vars by:
Collections.sort(vars);
As far as a Variable contains numOfConstraints, according to your code, you can make your Variable class implement Comparable interface, like
public class Variuable implements Comparable<Variable> {
private int numOfConstraints;
public int compareTo(Variable other){
if(this == other) { return 0; }
return (numOfConstraints == other.numOfConstraint) ? 0 : ((numOfConstraints > other.numOfConstraint) ? 1 : -1);
}
}
And then use the utility method java.util.Collections.sort(vars);, that's it.
Your Variable class should implement the Comparable interface,
When it does you should implement the compareTo method.
After that you can sort it by calling the Collection.sort method.
If you want to sort by a permutation if your indexes that's just a matter of creating a new ArrayList and mapping each index to the new index (using a for loop)
Here is such a (generic) method
public static <T> ArrayList<T> permutate(ArrayList<T> origin,int[] permutation){
ArrayList<T> result = new ArrayList<T>(permutation.length);
for(int j=0;j<permutation.length;j++){
result.add(null);
}
for(int i=0;i<permutation.length;i++){
result.set(i, origin.get(permutation[i]));
}
return result;
}
You can do myArrayList= permutate(myArrayList, new int{1,2,3});
Here is example usage in a more basic use case (integers):
public static void main(String... args){
ArrayList<Integer> origin = new ArrayList<>(4);
origin.add(1);
origin.add(2);
origin.add(3);
origin.add(4);
int[] per = new int[]{2,1,3,0};
origin = permutate(origin,per);
System.out.println(Arrays.toString(origin.toArray())); //prints [3,2,4,1], your permutation
}

Categories