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);
}
};
Related
This question already has answers here:
"Comparison method violates its general contract!"
(13 answers)
Closed 4 years ago.
I have this code, comparator for sorting values:
public static Comparator<Partikel> getFFsorted() {
Comparator comp = new Comparator<Partikel>() {
#Override
public int compare(Partikel s1, Partikel s2) {
if(s1.ff>s2.ff)
{
return 1;
}
else
{
return -1;
}
}
};
return comp;
}
but I have an error like this :
Exception in thread "main" java.lang.IllegalArgumentException:
Comparison method violates its general contract! at
java.util.TimSort.mergeHi(TimSort.java:868)
at java.util.TimSort.mergeAt(TimSort.java:485)
at java.util.TimSort.mergeCollapse(TimSort.java:410)
at java.util.TimSort.sort(TimSort.java:214)
at java.util.TimSort.sort(TimSort.java:173)
at java.util.Arrays.sort(Arrays.java:659)
at java.util.Collections.sort(Collections.java:217)
How do I fix it?
Your method doesn't handle the case of equal ff.
As a result compare(s1,s2) and compare(s2,s1) will both return -1 when s1.ff == s2.ff. This violates the contract of compare, which requires that sgn(compare(x, y)) == -sgn(compare(y, x)) for all x and y.
public int compare(Partikel s1, Partikel s2) {
if(s1.ff > s2.ff) {
return 1;
} else if (s1.ff < s2.ff) {
return -1;
} else {
return 0;
}
}
those ff members in the Partikel class look like something numeric to me...
so it would be better that you implement the static method from the wrapper class..
something like:
#Override
public int compare(Partikel p1.ff, Partikel p2) {
return Integer.compare(p1.ff, p2.ff);
//or
return Double.compare(p1.ff, p2.ff);
//or
return Long.compare(p1.ff, p2.ff);
//or
return Float.compare(p1.ff, p2.ff);
}
You don't handle the case where the values are equal.
Java has builtin functions to correctly compare primitive data types in the wrapper class for each primitive data type (Integer, Long, Float, Double, etc) which you can use:
Comparator comp = new Comparator<Partikel>() {
#Override
public int compare(Partikel s1, Partikel s2) {
return Integer.compare(s1.ff, f2.ff);
// Or Long.compare, Float.compare, Double.compare, etc.
// depending on the datatype of the field "ff".
}
};
This will happen if you have any NaN values in your comparator
For example:
public class Demo
{
public static void main(String[] args) {
double a = Double.NaN;
double b = Double.NaN;
System.out.println(a < b);
System.out.println(a > b);
}
}
All of these print false. So you could end up in a situation where two non-NaN values were both deemed "equal" to NaN, but one was greater than the other. Basically, you should work out how you want to handle NaN values. Also check that that really is the problem, of course... do you really want NaN values for your Partikel?
And also the default comparator has implementation to compare for three cases i.e
1.Greater than
2. Less than
3. Equal to
Since you are taking only two ,so try to segregate into three as follows :
public int compare(Partikel s1, Partikel s2) {
if(s1.ff>s2.ff)
return 1;
else if(s1.ff<s2.ff)
return -1;
//for equality
else
return 0;
}
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();
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())
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
};
}
}
I am trying to overwrite the default compareTo() method in java by writing my own and using implements comparable, however it seems that java is still using the default method.
I am trying to sort an array of Strings by length that I get from a .dat file, however it keeps sorting it by alphabetic order instead. I would appreciate it if someone could tell me what I am doing wrong as I cannot figure out why this does not work.
Thanks
import static java.lang.System.*;
import java.util.Arrays;
public class Word implements Comparable
{
private String word;
private String[] array;
public Word()
{
word = "";
}
public Word(String s)
{
word = s;
}
public void setWord(String s)
{
word = s;
}
public int compareTo(String rhs)
{
String temp = (String)rhs;
if(word.length() > temp.length())
return 1;
else if(word.length() < temp.length())
return -1;
return 0;
}
public void setSize(int size)
{
array = new String[size];
}
public void add(int spot, String other)
{
array[spot] = other;
}
public String[] sortByLength()
{
Arrays.sort(array);
return array;
}
public String toString()
{
return Arrays.toString(array);
}
}
Here is the class that contains the main method
import java.io.File;
import java.io.IOException;
import java.util.Scanner;
import java.util.Arrays;
import static java.lang.System.*;
public class Lab18d
{
public static void main( String args[] ) throws IOException
{
Scanner file = new Scanner(new File("lab18d.dat"));
int size = file.nextInt();
file.nextLine();
Word test = new Word();
test.setSize(size);
String word = "";
for(int i = 0; i < size; i++)
{
word = file.next();
test.setWord(word);
test.add(i, word);
}
test.sortByLength();
System.out.println(test);
}
}
Do yourself a favour: every time you override a method, add the #Override annotation to it. This will give you a compile error if you make a mistake in overriding the method, which is what is happening here. You are implementing it wrong, as Comparable (the "raw" form of Comparable<T> does not declare a method compareTo(String), it declares a method compareTo(Object).
To get it to compile as is, you would need to accept an Object instead of a String or implement Comparable<String> instead of Comparable.
But that would really be incorrect in most cases, because such a comparison is not symmetric: you can compare a Word to a String but not a String to a word.
Most likely you want to implement Comparable<Word> instead of Comparable and accept a Word to compareTo().
#Override
public int compareTo(Word other)
{
String temp = other.word;
//...
}
Note though that Comparable is only a really good fit when a type is intrinsically ordered (what the docs call a "natural order") like dates or numbers. Since you are not actually comparing the two words alphabetically (which would be the closest to a String's natural order) this is a better candidate for using an external comparator.
//since Word.word is a private member, this either needs to be nested inside of Word
//or Word.word would need to be given an accessor method
public static class LengthComparator implements Comparator<Word> {
#Override
public int compare(Word word1, Word word2) {
return Integer.valueOf(word1.word.length()).compareTo(word2.word.length());
}
}
Comparable is typed, but you're using the raw type. Try this:
public class Word implements Comparable<Word> { // Note: typing of Comparable
...
public int compareTo(Word rhs) { // Note: parameter is typed
String temp = rhs.word;
return word.length() - temp.length(); // Note: Simplification of code
}
}
Check the signature of compareTo method here
It should be int compareTo(Object o)
and you are giving public int compareTo(String rhs)
You can also add #Override annotation to your method. It will let you know if you are not following proper signature.
The short version: You need to use the Arrays.sort method taking a Comparator instead.
The long version: The line
Arrays.sort(array);
in the sortByLength method keeps calling the compareTo methods on the objects it's sorting - and those objects are strings! Instead, you need the line
Arrays.sort(array, new Comparator<String>() {
#Override
public int compare(String s1, String s2) {
if (s1.length() > s2.length())
return 1;
if (s1.length() < s2.length())
return -1;
return 0;
}
});
or you can create a separate class implementing Comparator<String> and use an instance of that as the second argument to Arrays.sort.