Best way for custom sorting in Java? - 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));
}

Related

How to implement compareTo for any types of comparable objects

I don’t know how I can compare 2 comparable objects without some other variable which tells me which is larger. The question is: Create a class called Max that provides a single class method called max. max takes two arguments to objects that can be compared—that is, that implement the Java Comparable interface as shown above. It returns a reference to whichever is larger. If the two objects are equal, you should return the first. How Comparable is implemented is up to each class, and your method will be called on multiple different kinds of objects.
It gives the int compareTo (Object other) method in the interface but I’m having trouble finding a solution.
public class Max implements Comparable
{
public int compareTo(Object other)
{
}
public static Comparable max(Comparable first, Comparable second)
{
int fi = first.compareTo(second);
if(fi >0)
return first;
else if (fi<0)
return second;
return first;
}
}
java.lang.AssertionError: Class should not implement Comparable: expected [false] but found [true]
That is one of the errors. But also I need help writing the compareTo method.
I deleted my previous answer because I think, imho, you are over complicating this. Since the two arguments to max have implemented the Comparable<T> interface, all you have to do is call it as:
int ret = first.compareTo(second);
Then return first or second like you are doing based on the value of ret. That way you don't need to know anything about how it was implemented. Perhaps you could get some clarification from either your instructor or someone else who is working on this (I presume it is for an assignment).
It would be worthwhile for you to create some test classes which implement the interface. You can just make up some variable that represents size.
The keyword for your question is generics. You might want to do some research and read something about it. Take a look at the following example. I've implemented the class Max as a static class to keep it simple:
import java.time.LocalDate;
public class MyTestClass{
public static void main(String args[]) {
Integer i = 16;
Integer j = 15;
Integer m = Max.max(i, j);
System.out.println(m);
String k = "aaaa";
String n = "zzzz";
String s = Max.max(k, n);
System.out.println(s);
LocalDate d = LocalDate.now();
LocalDate e = LocalDate.now().plusDays(2);
LocalDate f = Max.max(d , e);
System.out.println(f);
}
static class Max{
public static <T extends Comparable> T max(T first, T second) {
if (first.compareTo(second) >= 0)
return first;
else
return second;
}
}
}
As you can see, there is a class Max with a single method max which accepts two objects, for example two integers, two strings or two date objects.
Since all these classes implement the comparable interface, you can use the max method for all object types. The compiler then decides during the runtime which comapreTo method to call, that is the compareTo of the class Integer, String, LocalDate or whatever.

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

Sum of ArrayList for different types

Is it possible to write a single method total to do a sum of all elements of an ArrayList, where it is of type <Integer> or <Long>?
I cannot just write
public long total(ArrayList<Integer> list)
and
public long total(ArrayList<Long> list)
together as there will be an error of erasure, and Integer does not automatically extends to Long and vice versa... but the code inside is identical!
Yes, you can implement such a method, since both Integer and Long extend Number. For example you can use a wildcard type for the list element type:
public static long total(List<? extends Number> list) {
long sum = 0;
for (Number n : list) {
if (!(n instanceof Byte || n instanceof Short || n instanceof Integer || n instanceof Long)) {
throw new IllegalArgumentException();
}
sum += n.longValue();
}
return sum;
}
This only works for the integral types however, since the sum variable and the return value are of type long.
Ideally you would like to be able to also use the method with Floats and Doubles and return an object of the same type as the list element type, but this is not easy to do for two reasons:
The only thing you can do with a Number is to get its value as one of the primitive number types. You can not sum two of them in a number dependent way.
It is not possible to create a 0-object of the right class.
EDIT: Much later...
Just for fun, lets do this in a nice way for Java. The thing you have to do is to manually provide the two operations mentioned above. A kind of value with two such operation is usually called a monoid in the context of algebra and functional programming.
The problem can be solved by creating objects that represent the monoid operations:
interface MonoidOps<T> {
T id();
T op(T o1, T o2);
}
The total method can now be implemented to take an object of this type in addition to the list:
public static <T> T total(List<T> list, MonoidOps<T> ops) {
T sum = ops.id();
for (T e : list) {
sum = ops.op(e, sum);
}
return sum;
}
To provide MonoidOps implementations for the numeric classes, lets create a simple helper class:
class SimpleMonoidOps<T> implements MonoidOps<T> {
private final T idElem;
private final BinaryOperator<T> operation;
public SimpleMonoidOps(T idElem, BinaryOperator<T> operation) {
this.idElem = idElem;
this.operation = operation;
}
public T id() {
return idElem;
}
public T op(T o1, T o2) {
return operation.apply(o1, o2);
}
}
The MonoidOps implementations can now be written neatly like this:
static final MonoidOps<Integer> INT_MONOID_OPS = new SimpleMonoidOps<>(0, Integer::sum);
static final MonoidOps<Double> DOUBLE_MONOID_OPS = new SimpleMonoidOps<>(0.0, Double::sum);
And the total method would be called like this:
int sum = total(Arrays.asList(1, 2, 3), INT_MONOID_OPS);
You can also use streams in Java 8
public static <T extends Number> long sumList(List<T> list)
{
return list.stream().mapToLong(a -> a.longValue()).sum();
}
You can use Java´s generics for this
public <T extends Number> T total(List<T> list) {
T sum = 0;
for (T n : list) {
sum += n.longValue();
}
return sum;
}
Knowing that T will always be a Number, things are simplified. However, this solution could work also for Strings, if necessary. The only change would be in the extends part.

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

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