Get a boolean answer from a custom Comparator - java

I have learned how to create my own Comparator, for example, create a simple one to compare based on absolute value
class absComparator implements Comparator<Integer> {
public int compare(Integer a, Integer b) {
return a * a - b * b;
}
}
And of course, this can be used for a customized sort:
List<Integer> list_1 = new ArrayList<>(Arrays.asList(-3, -2, -1, 0, 1, 2));
list_1.sort(new absComparator());
>>> [0, -1, 1, -2, 2, -3]
So this is all good, but what if I want to just compare two Integers based on this comparator to give a boolean value?
// Currently:
System.out.println(-2>1);
>>> false
So how do I get a true by comparing -2 and 1, using absComparator?

Call compare directly and check the result against 0 using the same relational operator. If you want to check -2 > 1, pass those two numbers in as parameters and check if the result is > 0.
Comparator<Integer> comparator = new absComparator();
System.out.println(comparator.compare(-2, 1) > 0);

IMO, method like isGreaterThan, isLessThan should be added to Comparator interface.
Since we don't have these in this moment, we can create our own interface extending Comparator, and add default method isGreaterThan, isLessThan, as follow:
public interface EnhancedComparator<T> extends Comparator<T> {
default boolean isGreaterThan(T target, T compareTo) {
return this.compare(target, compareTo) > 0;
}
default boolean isLessThan(T target, T compareTo) {
return this.compare(target, compareTo) < 0;
}
}
public class AbsoluteValueComparator implements EnhancedComparator<Integer> {
#Override
public int compare(Integer a, Integer b) {
a = Math.abs(a);
b = Math.abs(b);
return a.compareTo(b);
}
}
public class EnhancedComparatorTest {
public static void main(String[] args) {
EnhancedComparator<Integer> absoluteValueComparator = new AbsoluteValueComparator();
System.out.println("2 greater than 3 " + absoluteValueComparator.isGreaterThan(2, 3));
System.out.println("-3 greater than -2 " + absoluteValueComparator.isGreaterThan(-3, -2));
System.out.println("2 less than 3 " + absoluteValueComparator.isLessThan(2, 3));
System.out.println("-3 less than -2 " + absoluteValueComparator.isLessThan(-3, -2));
}
}
P.S.
a * a may overflow, you may refer to my example for more robust comparison.
References
The Java™ Tutorials Default Method
Java Doc Comparator
How does Java handle integer underflows and overflows and how would you check for it?

Java does not support operator overloading.
That being said, you can easily define your own static method for it:
private static final Comparator<Integer> ABS_CMP = new AbsComparator();
public static boolean gtAbs (int a, int b) {
return ABS_CMP.compare(a, b) > 0;
}
And then import it statically and use it operator-like:
import static your.UtilityClass.gteAbs;
[...]
int a = 5;
int b = -6;
if (gtAbs(a,b)) {
// do work
}

First, your problem is that you are not using your comparator.
class absComparator implements Comparator<Integer> {
public int compare(Integer a, Integer b) {
return a * a - b * b;
}
}
absComparator comp = new absComparator();
if (comp.compare(-2,1)) > 0) {
System.out.println(true);
}
But your comparator has a more fundamental problem. The following also
prints true. This is due to integer overflow when you subtract the products.
if (comp.compare(12345678, 123456789) > 0) {
System.out.println(true);
}
To correct the problem, write your comparator as follows:
class absComparator implements Comparator<Integer> {
public int compare(Integer a, Integer b) {
// eliminate the need for products by
// getting abs (Math.abs() also available);
int a1 = a >= 0 ? a : -a;
int b1 = b >= 0 ? b : -b;
// but you still should not subract as overflow can
// still happen for large values of a1 and b1
return a1 > b1 ? 1 : a1 < b1 ? -1 : 0;
// or return Integer.compare(a1,b1);
// or if (a1 > b1) {
// return 1;
// }
// if (a1 < b1) {
// return -1;
// }
// return 0;
}
}

Use an instance of your comparator:
absComparator comp = new absComparator();
int result = comp.compare(-2, 1);
if (result > 0) {
// -2 > 1 (according to your comparator)
}

Related

Need to find position of maximum of three numbers

I am trying to find the position of the maximum of three numbers that the user inputs. I do not know how to find the position of this but I can find max of the numbers.
Here is the task:
Here is the method named getNumberOfMaxParam that takes three integer numbers and returns the position of the first maximum in the order of the method parameters.
UPDATE: I am trying to compare the int a.b.c but I get This method must return a result of type int error
Code:
import java.util.Scanner;
class App {
public static int getNumberOfMaxParam(int a, int b, int c) {
int firstMax = Math.max(a, b);
int highMax = Math.max(firstMax, c);
if (a == highMax) {
System.out.println("1");
} else if (b == highMax) {
System.out.println("2");
} else if (c == highMax) {
System.out.println("3");
} else {
return highMax;
}
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
final int a = scanner.nextInt();
final int b = scanner.nextInt();
final int c = scanner.nextInt();
System.out.print(getNumberOfMaxParam(a, b, c));
}
}
Now how do I find the position?
You can do this with the Stream api. You simply pass an array of values in the order you want. Then create an IntStream of the values and find the max. Then create an IntStream of the indices and find the first value in the array to see if it matches max, and return the index.
private static int getIndexOfMax(int... values) {
int max = IntStream.of(values).max().orElseThrow(IllegalStateException::new);
return IntStream.range(0, values.length)
.filter(index -> values[index] == max)
.findFirst().orElseThrow(IllegalStateException::new);
}
int index = getIndexOfMax(1, 2, 3);
assert index == 2;
**PS. It could be controversial if you supply same value for multiple inputs.**
public static int getNumberOfMaxParam(int a, int b, int c) {
int firstMax = Math.max(a, b);
int highMax = Math.max(firstMax, c);
return (highMax == a ? 1 : (highMax == b ? 2 : 3 );
}

Using a comparator in Java

I am Having a tuple <Rank[0] , Rank[1]> and wanted to sort this in increasing order Rank[0] can be considered as first digit and Rank[2] as a second digit.
Here is my function:
Arrays.sort(S,new Comparator<Tuples>() {
public int compare(Tuples a, Tuples b) {
// TODO Auto-generated method stub
return (a.Rank[0]==b.Rank[0]) ? ((a.Rank[1]<b.Rank[1]) ? 1:0) :((a.Rank[0]<b.Rank[0])? 1:0);
}
});
The above thing is not giving be Sorted Array while it's C equivalent works i.e
int cmp(struct suffix a, struct suffix b)
{
return (a.rank[0] == b.rank[0])? (a.rank[1] < b.rank[1] ?1: 0):
(a.rank[0] < b.rank[0] ?1: 0);
}
Why My java Sorting is not working. Please Help
static class Tuples{
int[] Rank = new int[2];
}
What about something like this:
public int compare(Tuples a, Tuples b) {
int index = a.Rank[0] == b.Rank[0] ? 1: 0;
return a.Rank[index] - b.Rank[index];
}
A little easier to read in my opinion.
According to the documentation of the Comparator interface, the compare method must return 1, 0 or -1, that are:
0 = the objects are equal
-1 (or any negative integer) = the
object a is less than object b
1 (or any positive integer) = the
object a is greater than object b
So your method must be something like below:
public int compare(Tuples a, Tuples b) {
if(a.Rank[0]==b.Rank[0]){
if(a.Rank[1]==b.Rank[1]){ // a == b
return 0;
}else if(a.Rank[1]<b.Rank[1]){ // a < b
return -1;
}else{ // a > b
return 1;
}
}else{ // a != b
if(a.Rank[0]<b.Rank[0]){ // a < b
return -1;
}else{ //a > b
return 1;
}
}
}
Or in short (and ugly way) it would be like this:
public int compare(Tuples a, Tuples b) {
return (a.Rank[0]==b.Rank[0])?((a.Rank[1]==b.Rank[1])?0:((a.Rank[1]<b.Rank[1])?-1:1)):((a.Rank[0]<b.Rank[0])?-1:1);
}
As SqueezyMo stated in his answer case less and negative integer is missing. One can utilize subtraction to get compact code.
Arrays.sort(arr, new Comparator<Tuples>() {
public int compare(Tuples a, Tuples b) {
return a.Rank[0] == b.Rank[0] ? a.Rank[1] - b.Rank[1] : a.Rank[0] - b.Rank[0];
}
});
Naturally everything can be done using ternary operators, it drives maintainers crazy.
public static void main(String[] args) {
Tuples a = new Tuples();
a.Rank = new int[]{1, 2};
Tuples b = new Tuples();
b.Rank = new int[]{0, 1};
Tuples c = new Tuples();
c.Rank = new int[]{1, 3};
Tuples[] arr = {a, b, c};
Arrays.sort(arr, new Comparator<Tuples>() {
#Override
public int compare(Tuples a, Tuples b) {
return a.Rank[0] == b.Rank[0]
? a.Rank[1] == b.Rank[1]
? 0 : a.Rank[1] < b.Rank[1]
? -1 : 1 : a.Rank[0] > b.Rank[0]
? 1 : -1;
}
});
for (int i = 0; i < 3; i++) {
System.out.println(arr[i].Rank[0] + ":" + arr[i].Rank[1]);
}
}
static class Tuples {
public int[] Rank;
}

Calling Comparator parameter in main method

I am implementing insertion sort method. Here is the requirement of my code.
The method insertionSort is a static method that returns nothing.
It has two parameters: a generic array and a Comparator (generic).
It sorts the generic array using the merge sort algorithms
My Question is: What do I use for Comparator parameter c when calling in main method?
Here is what I have so far, I have some unimplemented method (merge sort an isAnagaram) ignore those
public class Sorting
{
public static <T extends Comparable<T>> void insertionSort(T[] a, Comparator<T> c)
{
for (int i = 0; i < a.length; i++)
{
T key = a[i];
int j;
for (j = i - 1; j >= 0; j--)
{
if (c.compare(a[j], key) <= 0)
break;
a[j + 1] = a[j];
}
a[j + 1] = key;
}
}
public static void mergeSort()
{
//TODO
}
public static boolean isAnagram(String first, String second)
{
//TODO
return false;
}
public static void main(String[] args)
{
Integer a[] = { 99, 8, 19, 88, 62, 2, 1, 9, 19 };
// not sure how to pass parameter comparator
insertionSort(a, null );
for (int i = 0; i < a.length; i++)
{
System.out.print(a[i] + " ");
}
}
}
I looked around on stack overflow as well as googled a lot on a Comparator interface but I couldn't really find any method where you are required to pass Generic comparator as parameter. Can someone help me tell what I am not understanding or direct me to right direction.
Comparator is an interface, which can not be instantiated. You need to implement it. There are two methods to implement:
compare
equals
You need to implement them for Integer elements. Like this:
public class IntegerComparator implements Comparator {
public int compare(Integer a, Integer b) {
return a.intValue() - b.intValue();
}
public int equals(Object obj) {
return this.equals(obj);
}
}
and in your main you call it like this:
insertionSort(a, new IntegerComparator );
Explanation: Comparator is an interface, therefore it cannot be instantiated. You need to implement it. You have an array of Integer elements to sort, therefore you can implement an Integer Comparator. The compare method returns the subtraction of the int values. If a < b, then it is negative. If a == b, then it is 0. If a > b, then it is positive.
Read more here and here.

Why aren't my objects that implement Comparable sorted?

my simple example (compiled working code) just does not sort fruits by their weight.
import java.util.Arrays;
public class Test {
public static class Fruit implements Comparable<Fruit> {
public int weight = 0;
public Fruit(int w) { weight = w; }
// compare this fruit to a given fruit f
public int compareTo(Fruit f) {
return (weight > f.weight) ? 1 : 0;
}
}
public static void main(String[] args) {
// get some fruits (we intentionally create a box for 100 fruits)
Fruit[] fruits = new Fruit[100];
for (int i = 0; i < 10; i++) {
fruits[i] = new Fruit((int)(Math.random() * 50 + 1));
}
// sort fruits by weight
Arrays.sort(fruits, 0, 10);
// print fruit weights
for (int i = 0; i < 10; i++) {
System.out.print(fruits[i].weight + " ");
}
}
}
Why it is so?
Alright, in my problem (not about fruits), I have objects that are never pairwise equal, that is why I thought one object is either bigger or smaller than another. So how can I handle this situation when I know that 0 (objects are equal) will never happen?
compareTo must return one of 3 values:
>0 --> Bigger than
0 --> Equal
<0 --> Less than
Your compareTo method only returns 0 or 1; fix that.
Use the method public static int compare(int x, int y) from the class java.lang.Integer (since Java 7).
public int compareTo(Fruit f) {
return Integer.compare(weight, f.weight);
}
If weight is never negative then you can try
return weight - f.weight;
instead of
return (weight > f.weight) ? 1 : 0;
to sort from lowest to highest value.
The best approach is to use the JDK-supplied method for comparing int values, which also makes it crystal clear what the code is doing
public int compareTo(Fruit f) {
return Integer.compare(weight, f.weight);
}
Prior to version 7 java, you have two choices:
public int compareTo(Fruit f) {
return weight - f.weight; // terse, but slightly obtuse
}
public int compareTo(Fruit f) {
return new Integer(weight).compareTo(f.weight); // ugly, but supposedly clear
}
My preference is the subtraction, because once you understand it, it's clear from then on.
Your compareTo method should return -1, 0, 1
LESSER = -1;
EQUAL = 0;
BIGGER = 1;

java sorting with comparator and swap function

I need to sorting function with custom comparator and swap function. I can write one myself, but I'm wondering if someone else didn't already do it. Java runtime contains many specialized sorting function for sorting arrays of primitive types, objects etc., but none of them take swap function as an argument. Google search also didn't find anything useful.
public interface IntComparator
{
int compare(int a, int b);
}
public interface IntSwap
{
void swap(int a, int b);
}
public static void sort(IntComparator compFn, IntSwap swapFn, int off, int len);
Here is what I was looking for. It's based on java runtime algorithm for sorting integers. With proper implementation of Sortable interface, it can sort just about anything.
public class Sort {
public static void sort(Sortable sortable, int off, int len) {
// Insertion sort on smallest arrays
if (len < 7) {
for (int i = off; i < len + off; i++) {
for (int j = i; j > off && sortable.compare(j - 1, j) > 0; j--) {
sortable.swap(j, j - 1);
}
}
return;
}
// Choose a partition element, v
int m = off + (len >> 1); // Small arrays, middle element
if (len > 7) {
int l = off;
int n = off + len - 1;
if (len > 40) { // Big arrays, pseudomedian of 9
int s = len / 8;
l = med3(sortable, l, l + s, l + 2 * s);
m = med3(sortable, m - s, m, m + s);
n = med3(sortable, n - 2 * s, n - s, n);
}
m = med3(sortable, l, m, n); // Mid-size, med of 3
}
// Establish Invariant: v* (<v)* (>v)* v*
int a = off, b = a, c = off + len - 1, d = c;
while (true) {
while (b <= c && sortable.compare(b, m) <= 0) {
if (sortable.compare(b, m) == 0) {
sortable.swap(a, b);
m = a;
a++;
}
b++;
}
while (c >= b && sortable.compare(c, m) >= 0) {
if (sortable.compare(c, m) == 0) {
sortable.swap(c, d);
m = d;
d--;
}
c--;
}
if (b > c) {
break;
}
sortable.swap(b++, c--);
}
// Swap partition elements back to middle
int s, n = off + len;
s = Math.min(a - off, b - a);
vecswap(sortable, off, b - s, s);
s = Math.min(d - c, n - d - 1);
vecswap(sortable, b, n - s, s);
// Recursively sort non-partition-elements
if ((s = b - a) > 1) {
sort(sortable, off, s);
}
if ((s = d - c) > 1) {
sort(sortable, n - s, s);
}
}
private static int med3(Sortable sortable, int a, int b, int c) {
return sortable.compare(a, b) < 0 ? (sortable.compare(b, c) < 0 ? b : sortable.compare(a, c) < 0 ? c : a)
: sortable.compare(b, c) > 0 ? b : sortable.compare(a, c) > 0 ? c : a;
}
private static void vecswap(Sortable sortable, int a, int b, int n) {
for (int i = 0; i < n; i++, a++, b++) {
sortable.swap(a, b);
}
}
}
I need to swap indices in two arrays. I know that I could sort twodimensional array but that would increase required memory.
No. If I understand you correctly, it does not result in any overhead.
Remember that Java does not store arrays or objects directly in variables (or arrays!). It stores references. Even if each element referred to from an array is 40 bytes large, it will be stored as a reference in the array.
Thus, I suggest you go with the built in sorting mechanisms. They won't shuffle around lots of data, only the references.
Because sort() for an array of Object is stable, you may be able to get useful information inside a custom Comparator. This one counts swaps while sorting by String length.
import java.util.Arrays;
import java.util.Comparator;
/** #see http://stackoverflow.com/questions/4983746 */
public class SortTest {
private static class LengthComparator implements Comparator<String> {
private int count;
public int compare(String s1, String s2) {
int a = s1.length();
int b = s2.length();
if (a < b) {
return -1;
} else if (a > b) {
count++;
return 1;
} else {
return 0;
}
}
}
public static void main(String[] args) throws Exception {
String[] sa = {"One", "Two", "Three", "Four", "Five"};
System.out.println(Arrays.toString(sa));
LengthComparator byLength = new LengthComparator();
Arrays.sort(sa, byLength);
System.out.println(Arrays.toString(sa));
System.out.println(byLength.count);
}
}
Console:
[One, Two, Three, Four, Five]
[One, Two, Four, Five, Three]
2
Regarding to swap: Java passed argument by value, so methods swap(int a, int b) and swap(Object a, Object b) don't work as expected.
If you propose these interfaces, at least add some comments to what they should do. From the discussion I got that you want something like this:
/**
* A Sortable represents a indexed collection of comparable
* elements.
* It does not offer direct access to its elements, only
* comparison and swapping by indices.
*
* In the method specifications we are using this[i] to
* mean the
*/
public interface Sortable {
/**
* Compares two elements by their indices.
* #return -1 if this[first] < this[second],
* 0 if this[first] = this[second]
* 1 if this[first] > this[second]
* #throws IndexOutOfBoundsException if one
* or both indices are outside of the
* limits of this sequence.
*/
public int compare(int first, int second);
/**
* Swaps two elements by their indices.
* This is roughly equivalent to this sequence:
* <pre>
* temp = this[first];
* this[first] = this[second];
* this[second] = temp;
* </pre>
*/
public void swap(int first, int second);
}
interface Sorter {
/**
* sorts an interval of a sequence.
* #param sequence the sequence to be sorted.
* #param off the start of the interval to be sorted.
* #param the length of the interval to be sorted.
*/
public void sort(Sortable sequence, int off, int len);
}
And then you could have your sort algorithm implement Sorter, while your data structure implements Sortable.
Of course one could split the both functions of Sortable in an IndexComparator and IndexSwapper (not Int... like you named them), but they are both directly coupled to your data structure (consisting of your two arrays).

Categories

Resources