How to correctly initialize a Comparator? - java

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

Related

Use of anonymous class in sort method

why if I put an anonymous class with Comparator in the sort method of List the compiler show me an error?
My code:
public class Example2 {
public static void main(String[] args) {
List<String> l = Arrays.asList("a","b","c","d");
l.sort(Comparator<String> c= new Comparator<>() { //compiler error
public int compare(String a, String b) {
return b.compareTo(a);
}
});
}
The sort method accepts a Comparator. If I write this code, it compiles:
public class Example2 {
public static void main(String[] args) {
List<String> l = Arrays.asList("a","b","c","d");
l.sort(new Comparator<String>() { //it's ok
public int compare(String a, String b) {
return b.compareTo(a);
}
});
}
Or this code:
public class Example2 {
public static void main(String[] args) {
List<String> l = Arrays.asList("a","b","c","d");
Comparator <String> c = new Comparator<String>() {
public int compare(String a, String b) {
return b.compareTo(a);
}
};
l.sort(c); //it's ok
}
Why does it happen?
Thanks a lot!
The first one fails as it is an assignment. The sort method expects an object of the Comparator class. So when you say sort(new Comparator), you are creating a new Comparator and immediately passing it to the sort method. When you have Comparator c = new Comparator () and then you have sort(c), you create an new Comparator, store it in variable c, and pass it to the sort method.
The first segment of code tries to both assign a new Comparator to variable c and pass it to method sort, the syntax of java does not allow this. It is analagous to having a method that takes one integer as argument and writing foo(int bar = 7). It doesnt quite make sense.
In the second example you are not giving the comparator to the method, you are assigning it to the 'c' reference. In the third example you are assigning it to c reference but then giving it to the sort method.
Hope this helps :)
The problem here is not that you are passing an anonymous class.
l.sort(Comparator<String> c= new Comparator<>() { ... });
This is attempting to declare a variable, c. Variable declarations are statements, and so they cannot be used inside expressions. (You also can't use the diamond operator when declaring an anonymous class).
If you want to declare a variable to hold that comparator, and assign it inside the sort invocation, you can, like so:
Comparator<String> c;
l.sort(c = new Comparator<String>() { ... });
but I suspect that isn't what you intended.

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 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
}

Java get String CompareTo as a comparator object

I would like to sort and binary search a static array of strings via the String.CompareTo comparator.
The problem is that both sorting, and binary searching requires that a Comparator object be passed in -- So how do I pass in the built in string comparator?
You may write your own comparator
public class ExampleComparator implements Comparator<String> {
public int compare(String obj1, String obj2) {
if (obj1 == obj2) {
return 0;
}
if (obj1 == null) {
return -1;
}
if (obj2 == null) {
return 1;
}
return obj1.compareTo(obj2);
}
}
Solution for Java 8 based on java.util.Comparator.comparing(...):
Comparator<String> c = Comparator.comparing(String::toString);
or
Comparator<String> c = Comparator.comparing((String x) -> x);
The Arrays class has versions of sort() and binarySearch() which don't require a Comparator. For example, you can use the version of Arrays.sort() which just takes an array of objects. These methods call the compareTo() method of the objects in the array.
Ok this is a few years later but with java 8 you can use Comparator.naturalOrder():
http://docs.oracle.com/javase/8/docs/api/java/util/Comparator.html#naturalOrder--
From javadoc:
static <T extends Comparable<? super T>> Comparator<T> naturalOrder()
Returns a comparator that compares Comparable objects in natural order.
The returned comparator is serializable and throws NullPointerException when comparing null.
If you do find yourslef needing a Comparator, and you already use Guava, you can use Ordering.natural().
This is a generic Comparator for any kind of Comparable object, not just String:
package util;
import java.util.Comparator;
/**
* The Default Comparator for classes implementing Comparable.
*
* #param <E> the type of the comparable objects.
*
* #author Michael Belivanakis (michael.gr)
*/
public final class DefaultComparator<E extends Comparable<E>> implements Comparator<E>
{
#SuppressWarnings( "rawtypes" )
private static final DefaultComparator<?> INSTANCE = new DefaultComparator();
/**
* Get an instance of DefaultComparator for any type of Comparable.
*
* #param <T> the type of Comparable of interest.
*
* #return an instance of DefaultComparator for comparing instances of the requested type.
*/
public static <T extends Comparable<T>> Comparator<T> getInstance()
{
#SuppressWarnings("unchecked")
Comparator<T> result = (Comparator<T>)INSTANCE;
return result;
}
private DefaultComparator()
{
}
#Override
public int compare( E o1, E o2 )
{
if( o1 == o2 )
return 0;
if( o1 == null )
return 1;
if( o2 == null )
return -1;
return o1.compareTo( o2 );
}
}
How to use with String:
Comparator<String> stringComparator = DefaultComparator.getInstance();
Again, don't need the comparator for Arrays.binarySearch(Object[] a, Object key) so long as the types of objects are comparable, but with lambda expressions this is now way easier.
Simply replace the comparator with the method reference: String::compareTo
E.g.:
Arrays.binarySearch(someStringArray, "The String to find.", String::compareTo);
You could also use
Arrays.binarySearch(someStringArray, "The String to find.", (a,b) -> a.compareTo(b));
but even before lambdas, there were always anonymous classes:
Arrays.binarySearch(
someStringArray,
"The String to find.",
new Comparator<String>() {
#Override
public int compare(String o1, String o2) {
return o1.compareTo(o2);
}
});
Also, if you want case-insensitive comparison, in recent versions of Java the String class contains a public static final field called CASE_INSENSITIVE_ORDER which is of type Comparator<String>, as I just recently found out. So, you can get your job done using String.CASE_INSENSITIVE_ORDER.
We can use the String.CASE_INSENSITIVE_ORDER comparator to compare the strings in case insensitive order.
Arrays.binarySearch(someStringArray, "The String to find.",String.CASE_INSENSITIVE_ORDER);
To generalize the good answer of Mike Nakis with String.CASE_INSENSITIVE_ORDER, you can also use :
Collator.getInstance();
See Collator
Regarding Nambari's answer there was a mistake. If you compare values using double equal sign == program will never reach compare method, unless someone will use new keyword to create String object which is not the best practice. This might be a bit better solution:
public int compare(String o1, String o2) {
if (o1 == null && o2 == null){return 0;}
if (o1 == null) { return -1;}
if (o2 == null) { return 1;}
return o1.compareTo(o2);
}
P.S. Thanks for comments ;)
You can use the StringUtils.compare("a", "b")

compareTo() method is not overriding default method when using Comparable interface

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.

Categories