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

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.

Related

How do you create a constructor that takes an array of "Range" objects and initializes the list, list is intialized to an ArrayList

I am having trouble creating a constructor that takes an array of Range objects and initializes the list – list should be initialized to an ArrayList of Range. This is the code I have so far from my classes. This constructor method I am trying to create belongs in the multipleGroups Method. I have searched through stackoverflow with no luck for any similar questions but had no luck. Any help is appreciated.
public interface NumberGroup
{
boolean contains(int value);
}
import java.util.Scanner
public class Range implements NumberGroup
{
private int minValue, maxValue;
public Range(int minValue, int maxValue)
{
this.minValue = minValue;
this.maxValue = maxValue;
}
public boolean contains(int value)
{
return minValue <= value && value <= maxValue;
}
}
import java.util.List
import java.util.ArrayList
public class MultipleGroups implements NumberGroup
{
private List<NumberGroup> groupList;
//problem area here.
public MultipleGroups(){
}
public boolean contains(int num)
{
for(NumberGroup group : groupList)
if(group.contains(num))
return true;
return false;
}
the test class tests the constructor with the following:
Range [] myRanges = new Range[3];
myRanges[0] = new Range(5,8);
myRanges[1] = new Range(10,12);
myRanges[2] new Range(1, 6);
group = new MultipleGroups(myRanges);
The following appears to satisfy your requirements:
public MultipleGroups(Range[] ranges){
this.groupList = Arrays.asList(ranges);
}
There are a few things you might consider doing rather than just using this as-is:
You may want to allow varargs invocation (i.e. that you don't have to explicitly create the array at the call site):
public MultipleGroups(Range... ranges){
and you may want to copy the list in order to avoid callers doing nefarious things to the array after they call the constructor:
this.groupList = new ArrayList<>(Arrays.asList(ranges));

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

Best way for custom sorting in Java?

I am a C++ programmer and I am using Java at the moment (I do have a considerable amount of java experience).
Basically, I want to recreate the pair<int,int> that I so commonly use in C++ and I want to have it sorted by the second integer value.
I am searching up on the internet and trying different ways of going about this, including using Comparator, Comparable etc.
I am basically creating a test program that looks like this:
import java.math.*;
import java.util.*;
import java.io.*;
import java.text.*;
class PairTest
{
public static void main (String args[]) // entry point from OS
{
new PairTest().run();
}
public void run (){
Pair foo = new Pair(1,2);
System.out.println(foo.first + " "+ foo.second);
ArrayList <Pair> al = new ArrayList<Pair>();
for(int i =10;i>0;i--){
al.add(new Pair(i, i*2));
}
for(int i =0;i<al.size();i++){
System.out.println(al.get(i).first + " " + al.get(i).second);
}
Collections.sort(al);
for(int i =0;i<al.size();i++){
System.out.println(al.get(i).first + " " + al.get(i).second);
}
}
private class Pair implements Comparable{
public int first;
public int second;
public Pair (int a, int b){
this.first = a;
this.second = b;
}
int compareTo (Pair o){
return new Integer(this.second).compareTo(new Integer(o.second));
}
}
}
What would be the best way to go about making a custom sorting function so the ArrayList sorts by the "second" variable. I want a quick and safe way of doing it, and at the moment, the compiler is telling me that "PairTest.Pair does not override abstract method compareTo..."
I really don't know whats going on, any help would be greatly appreciated.
There are two problems with your Pair class: it does not declare a generic parameter and the compareTo method needs to be public. Also, it is more efficient to just return the difference between int values than to construct Integer objects and invoke compareTo. Try this:
private class Pair implements Comparable<Pair> {
public int first;
public int second;
public Pair (int a, int b){
this.first = a;
this.second = b;
}
public int compareTo (Pair o){
return second < o.second ? -1 : (second == o.second ? 0 : 1);
}
}
In your code, you should change:
private class Pair implements Comparable
to
private class Pair implements Comparable<Pair>
And you change this line:
int compareTo (Pair o)
to
public int compareTo (Pair o)
because this function will be use outside of this class :)
That's all you need :)
Override comapreTo method in your Pair class. No need to implement anything.
comapreTo method accepts Object as the argument
public int compareTo(Object another)
{
return new Integer(this.second).compareTo(new Integer(((Pair)another).second));
}

How find if arraylist contains a value which is in class field?

I have class:
static class AnotherClass {
int number;
String isPrime;
public int getNumber() {
return number;
}
public String getPrime() {
return isPrime;
}
public void setNumber(int number) {
this.number = number;
}
public void setPrime(String prime) {
isPrime = prime;
}
}
and in main class i have:
List<AnotherClass> listx = new ArrayList<AnotherClass>();//just a arraylist
for (int z = 0; z < howManyQuestions; z++) {//in loop i add class with fields
AnotherClass classx = new AnotherClass();
int valuex = Integer.parseInt(keyboardkey.readLine());
classx.setNumber(valuex);//save value in this class
String answer = Check(valuex);//i just get here string answer YES NO
classx.setPrime(answer);
listx.add(classx);//and i add this two fields of class to list
System.out.println();
}
INPUT: (i add value and check if it was input before)
3
4
3
OUTPUT
NO
NO
YES
How can i check if, for example value "3" is containing by list?
1 AnotherClass must implement equals() (and implement hashCode() accordingly).
2 Use method contains(Object o) from listx.
private boolean contains(int i)
{
for(int j: listx.getNumber()) { if(i == j) return true; }
return false;
}
A Few notes -
your class doesn't require static. That has a use if you're declaring an inner class.
You have a container class holding a string that's dependant on an int, as well as the int. It'd be more idiomatic to have the check inside your class, e.g.
class AnotherClass {
int number;
public int getNumber() {
return number;
}
public String getPrime() {
return check(number)
}
private boolean check() { ... whatever logic you had .. }
}
If you're looking for "contains" functionality, you'd probably use a HashSet, or a LinkedHashSet( if you want to preserve the ordering ). If you want to do this with your created class you'll need to implement a hashCode() method to tell the hashSet how to know if it has a duplicate value.
Or you can just iterate over your list.
You have to implement equals() for AnotherClass. The default equals() compares identity instead of value equality.
The javadoc for List.contains() says:
Returns true if this list contains the specified element. More formally, returns true if and only if this list contains at least one element e such that (o==null ? e==null : o.equals(e)).

Categories