I have such code:
BigDecimal n1= BigDecimal.TEN;
Long n2= 15L;
nullOrNonPositive(n1);
nullOrNonPositive(n2);
And method code:
private boolean nullOrNonPositive(Comparable value) {
return isNull(value) || value.compareTo(BigDecimal.ZERO) <= 0;
}
I do get an Exception that Long cannot be compared to BigDecimal, which is pretty obvious. Is there a way to make the method generic and compare Comparable, which will always be a number (but different class), to zero?
I would take a parameter that denotes the zero value for the type represented by the Comparator and let the caller pass it.
private static <T> boolean nullOrNonPositive(Comparable<T> value, T zero) {
return isNull(value) || value.compareTo(zero) <= 0;
}
Call it as
nullOrNonPositive(n1, BigDecimal.ZERO); //false
nullOrNonPositive(n2, 0L); //false
nullOrNonPositive(-1L, 0L); //true
nullOrNonPositive(2, 0)); //true
nullOrNonPositive("1", "0"); //false
nullOrNonPositive("0", "0"); //true
If the set of possible types (T) is limited, you can stores the zero values in a map.
Building upon user7's answer, if you don't want to have to pass the zero value, you can add methods that call the main function. This frees the caller from having to know what the zero value is for each type.
private static <T> boolean nullOrNonPositive(Comparable<T> value, T zero) {
return isNull(value) || value.compareTo(zero) <= 0;
}
private boolean nullOrNonPositive(BigDecimal value) {
return nullOrNonPositive(value, BigDecimal.ZERO)
}
private boolean nullOrNonPositive(Long value) {
return nullOrNonPositive(value, 0L);
}
Or if you don't want to define the 0 for each number, and you always use nullOrNonPositive for numbers, you could do:
private boolean nullOrNonPositive(Number value) {
return isNull(value) || new BigDecimal(value.toString()).signum() <= 0;
}
This question already has answers here:
Compare two objects with "<" or ">" operators in Java
(4 answers)
Closed 6 years ago.
Consider having this class in Java:
public class Foo implements Comparable<Foo> {
private int someValue;
#Override
public int compareTo(Foo o) {
if (this.someValue < o.someValue) {
return -1;
} else if (this.someValue == o.someValue) {
return 0;
} else {
return 1;
}
}
}
I tried to do this:
Foo foo1 = new Foo(someValue);
Foo foo2 = new Foo(someAnotherValue);
if (foo1 < foo2) {
// do something
}
But the IDE is giving me an error which is: "Bad operand types for binary operation '<' first type: Foo, second type: Foo"
May you please tell me what is wrong?
Thanks in advance for your help,
Implementing the Comparable interface is a good first step to being able to compare your objects properly. However, that doesn't allow you to use the < and > comparison operators as if the operators were overloaded. You just need to call your compareTo method.
if (foo1.compareTo(foo2) < 0) {
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);
}
};
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)).
I want to compare to variables, both of type T extends Number. Now I want to know which of the two variables is greater than the other or equal. Unfortunately I don't know the exact type yet, I only know that it will be a subtype of java.lang.Number. How can I do that?
EDIT: I tried another workaround using TreeSets, which actually worked with natural ordering (of course it works, all subclasses of Number implement Comparable except for AtomicInteger and AtomicLong). Thus I'll lose duplicate values. When using Lists, Collection.sort() will not accept my list due to bound mismatchs. Very unsatisfactory.
This should work for all classes that extend Number, and are Comparable to themselves. By adding the & Comparable you allow to remove all the type checks and provides runtime type checks and error throwing for free when compared to Sarmun answer.
class NumberComparator<T extends Number & Comparable> implements Comparator<T> {
public int compare( T a, T b ) throws ClassCastException {
return a.compareTo( b );
}
}
A working (but brittle) solution is something like this:
class NumberComparator implements Comparator<Number> {
public int compare(Number a, Number b){
return new BigDecimal(a.toString()).compareTo(new BigDecimal(b.toString()));
}
}
It's still not great, though, since it counts on toString returning a value parsable by BigDecimal (which the standard Java Number classes do, but which the Number contract doesn't demand).
Edit, seven years later: As pointed out in the comments, there are (at least?) three special cases toString can produce that you need to take into regard:
Infinity, which is greater than everything, except itself to which it is equal
-Infinity, which is less than everything, except itself to which it is equal
NaN, which is extremely hairy/impossible to compare since all comparisons with NaN result in false, including checking equality with itself.
After having asked a similar question and studying the answers here, I came up with the following. I think it is more efficient and more robust than the solution given by gustafc:
public int compare(Number x, Number y) {
if (isSpecial(x) || isSpecial(y))
return Double.compare(x.doubleValue(), y.doubleValue());
else
return toBigDecimal(x).compareTo(toBigDecimal(y));
}
private static boolean isSpecial(Number x) {
var specialDouble = x instanceof Double d
&& (Double.isNaN(d) || Double.isInfinite(d));
var specialFloat = x instanceof Float f
&& (Float.isNaN(f) || Float.isInfinite(f));
return specialDouble || specialFloat;
}
private static BigDecimal toBigDecimal(Number number) {
if (number instanceof BigDecimal d)
return d;
if (number instanceof BigInteger i)
return new BigDecimal(i);
if (number instanceof Byte || number instanceof Short
|| number instanceof Integer || number instanceof Long)
return new BigDecimal(number.longValue());
if (number instanceof Float || number instanceof Double)
return new BigDecimal(number.doubleValue());
try {
return new BigDecimal(number.toString());
} catch(NumberFormatException e) {
throw new RuntimeException("The given number (\"" + number + "\" of class " + number.getClass().getName() + ") does not have a parsable string representation", e);
}
}
One solution that might work for you is to work not with T extends Number but with T extends Number & Comparable. This type means: "T can only be set to types that implements both the interfaces."
That allows you to write code that works with all comparable numbers. Statically typed and elegant.
This is the same solution that BennyBoy proposes, but it works with all kinds of methods, not only with comparator classes.
public static <T extends Number & Comparable<T>> void compfunc(T n1, T n2) {
if (n1.compareTo(n2) > 0) System.out.println("n1 is bigger");
}
public void test() {
compfunc(2, 1); // Works with Integer.
compfunc(2.0, 1.0); // And all other types that are subtypes of both Number and Comparable.
compfunc(2, 1.0); // Compilation error! Different types.
compfunc(new AtomicInteger(1), new AtomicInteger(2)); // Compilation error! Not subtype of Comparable
}
The most "generic" Java primitive number is double, so using simply
a.doubleValue() > b.doubleValue()
should be enough in most cases, but... there are subtle issues here when converting numbers to double. For example the following is possible with BigInteger:
BigInteger a = new BigInteger("9999999999999992");
BigInteger b = new BigInteger("9999999999999991");
System.out.println(a.doubleValue() > b.doubleValue());
System.out.println(a.doubleValue() == b.doubleValue());
results in:
false
true
Although I expect this to be very extreme case this is possible. And no - there is no generic 100% accurate way. Number interface have no method like exactValue() converting to some type able to represent number in perfect way without loosing any information.
Actually having such perfect numbers is impossible in general - for example representing number Pi is impossible using any arithmetic using finite space.
What about this one? Definitely not nice, but it deals with all necessary cases mentioned.
public class SimpleNumberComparator implements Comparator<Number>
{
#Override
public int compare(Number o1, Number o2)
{
if(o1 instanceof Short && o2 instanceof Short)
{
return ((Short) o1).compareTo((Short) o2);
}
else if(o1 instanceof Long && o2 instanceof Long)
{
return ((Long) o1).compareTo((Long) o2);
}
else if(o1 instanceof Integer && o2 instanceof Integer)
{
return ((Integer) o1).compareTo((Integer) o2);
}
else if(o1 instanceof Float && o2 instanceof Float)
{
return ((Float) o1).compareTo((Float) o2);
}
else if(o1 instanceof Double && o2 instanceof Double)
{
return ((Double) o1).compareTo((Double) o2);
}
else if(o1 instanceof Byte && o2 instanceof Byte)
{
return ((Byte) o1).compareTo((Byte) o2);
}
else if(o1 instanceof BigInteger && o2 instanceof BigInteger)
{
return ((BigInteger) o1).compareTo((BigInteger) o2);
}
else if(o1 instanceof BigDecimal && o2 instanceof BigDecimal)
{
return ((BigDecimal) o1).compareTo((BigDecimal) o2);
}
else
{
throw new RuntimeException("Ooopps!");
}
}
}
This should work for all classes that extend Number, and are Comparable to themselves.
class NumberComparator<T extends Number> implements Comparator<T> {
public int compare(T a, T b){
if (a instanceof Comparable)
if (a.getClass().equals(b.getClass()))
return ((Comparable<T>)a).compareTo(b);
throw new UnsupportedOperationException();
}
}
if(yourNumber instanceof Double) {
boolean greaterThanOtherNumber = yourNumber.doubleValue() > otherNumber.doubleValue();
// [...]
}
Note: The instanceof check isn't necessarily needed - depends on how exactly you want to compare them. You could of course simply always use .doubleValue(), as every Number should provide the methods listed here.
Edit: As stated in the comments, you will (always) have to check for BigDecimal and friends. But they provide a .compareTo() method:
if(yourNumber instanceof BigDecimal && otherNumber instanceof BigDecimal) {
boolean greaterThanOtherNumber = ((BigDecimal)yourNumber).compareTo((BigDecimal)otherNumber) > 0;
}
You can simply use Number's doubleValue() method to compare them; however you may find the results are not accurate enough for your needs.
Let's assume that you have some method like:
public <T extends Number> T max (T a, T b) {
...
//return maximum of a and b
}
If you know that there are only integers, longs and doubles can be passed as parameters then you can change method signature to:
public <T extends Number> T max(double a, double b) {
return (T)Math.max (a, b);
}
This will work for byte, short, integer, long and double.
If you presume that BigInteger's or BigDecimal's or mix of floats and doubles can be passed then you cannot create one common method to compare all these types of parameters.
If your Number instances are never Atomic (ie AtomicInteger) then you can do something like:
private Integer compare(Number n1, Number n2) throws SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException {
Class<? extends Number> n1Class = n1.getClass();
if (n1Class.isInstance(n2)) {
Method compareTo = n1Class.getMethod("compareTo", n1Class);
return (Integer) compareTo.invoke(n1, n2);
}
return -23;
}
This is since all non-Atomic Numbers implement Comparable
EDIT:
This is costly due to reflection: I know
EDIT 2:
This of course does not take of a case in which you want to compare decimals to ints or some such...
EDIT 3:
This assumes that there are no custom-defined descendants of Number that do not implement Comparable (thanks #DJClayworth)
In my use case, I was looking for a general Comparator that works with the autoboxed primitives (64 bit max precision), not arbitrary precision types like BigInteger and BigDecimal. Here's a first shot at it..
public class PrimitiveComparator implements Comparator<Number> {
#Override
public int compare(Number a, Number b) {
if (a == b)
return 0;
double aD = a.doubleValue();
double bD = b.doubleValue();
int comp = Double.compare(aD, bD);
if (comp == 0 && inLongBounds(aD))
comp = Long.compare(a.longValue(), b.longValue());
return comp;
}
private boolean inLongBounds(double value) {
return
Double.compare(value, Long.MAX_VALUE) <= 0 &&
Double.compare(value, Long.MIN_VALUE) >= 0;
}
}
The objective is to be able to compare mixed types (e.g. Floats against Longs). This should also work with those AtomicXxx types (or any hand rolled Number subclass that uses no more than 64 bits).
In this ordering, btw, Double.NaN > Double.POSITVE_INFINITY > { everything else }.