I have a class, which I have simplified to this:
final class Thing {
private final int value;
public Thing(int value) {
this.value = value;
}
public int getValue() {
return value;
}
@Override public String toString() {
return Integer.toString(value);
}
}
I want to sort an array of this thing. So I have created a simple copmarator:
private static final Comparator<Thing> reverse = new Comparator<Thing>() {
public int compare(Thing a, Thing b) {
return a.getValue() - b.getValue();
}
};
I then use the two argument form of Arrays.sort
.
This works fine for my test cases, but sometimes it goes all wrong with the array ending up in a strange but repeatable order. How can this be?
-
You cannot use minus to create the comparison. You'll overflow when the absolute difference exceeds
Integer.MAX_VALUE
.Instead, use this algorithm:
int compareInts( int x, int y ) { if ( x < y ) return -1; if ( x > y ) return 1; return 0; }
I like to have this function in a library for such purposes.
Tom Hawtin - tackline : For a moment I was going to point you to the static method in Integer. But it isn't there...Grundlefleck : @Tom: `Integer.valueOf(x).compareTo(y);` is the most succinct way I can think of. Strange how Double has the static `compare()` method and the other number types don't.Jason Cohen : @Grundlefleck: True! But of course my method is much faster to execute because it doesn't create a new `Integer` instance. -
What kind of numbers do you throw in there? If your numbers are large enough, you could wrap through the MIN/MAX values for integers and end up in a mess.
-
Integer overflow… or more precisely, underflow.
Instead, do an explicit comparison:
private static final Comparator<Thing> reverse = new Comparator<Thing>() { public int compare(Thing a, Thing b) { int av = a.getValue(), bv = b.getValue(); return (av == bv) ? 0 : ((av < bv) ? -1 : +1); } };
Using subtraction is fine if you are sure that the difference won't "wrap around". For example, when the values in question are constrained to be non-negative.
-
If a's value is very negative and b's value is very positive your answer will be very wrong.
IIRC, Int overflow silently wraps around in the JVM
-- MarkusQ
-
try
System.out.println(Integer.MAX_Value - Integer.MIN_VALUE);
This needs to return a positive number as MAX_VALUE > MIN_VALUE but instead prints -1
Stefan Kendall : +1 for the most non-obvious "wrapping" fail case. -
When comparing Java primitives, it is advisable to convert them to their Object counterparts and rely on their
compareTo()
methods.In this case you can do:
return Integer.valueOf(a.getValue()).compareTo(b.getValue())
When in doubt, use a well-tested library.
Tom Hawtin - tackline : Bit of an overhead there (for non-small values).
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.