Java Puzzlers

  As we all know, there is no system that works perfectly… never mind, Java is that kind of system too. Every system has his own crutches, errors and unexpected shutdowns (sometimes without explaining the reason, yeah, iPhone users, I am talking about you!).

  Today we will talk about Java pitfalls, corner cases and traps.

  To be honest, I was inspired by the Joshua Bloch & Neal Gafter book to write this article, Java Puzzlers: Traps, Pitfalls, and Corner Cases. I was reading it and got an idea that this can be not only interesting to read, but also worth sharing.

Oddity check

  The first situation that I would like to explain, it’s about the oddity check.

  How do we usually check if a number is odd? Easy, if the division by 2 gives us a remainder of 1, then the number is odd, isn’t it? Below is the method that does this.

public static boolean isOdd(int i){
     return i % 2 == 1;
}

public static void main(String[] args){
     System.out.println(isOdd(11));
     System.out.println(isOdd(-11));
}

  After running this code, here’s what we will see in the output:

>>>> true
>>>> false

  Wait, what?

  Unfortunately, it doesn’t work as we expected it to. This method returns a wrong answer one quarter of the time. Why one quarter? Because half of all int values are negative, and the isOdd method fails for all negative odd values. It returns false when invoked on any negative value, whether even or odd.

  Let’s dig deeper, how does Java remainder operator work?

  a - ( (a/b) * b ) == a

  Okay. How can we fix it? Let’s see…

public static boolean isOdd(int i){
     return i % 2 != 0;
}

  Now, running that code again will give us the following output:

>>>> true
>>>> true

  It’s fixed, we are brilliant!

Decimal place accuracy

  All right, let’s move on! Imagine you are on your way home from a volleyball game and suddenly one of your car’s headlights burned out. Sad. You stop at the closest gas station to buy a new one. Luckily they cost only $1.10 and you got $2 bill. So you expect to get your 90 cents back.

public static void main(String[] args){
     System.out.println(2.00 - 1.10);
}

  After executing the code above we get this:

>>>> 0.8999999999999999

  Hmm, it doesn’t look good? You were too naive to expect 0.90 in this article. Anyway, how could it know that you wanted two digits after the decimal point?

  If you would’ve read about the rules of converting double values to strings, which are specified in the documentation of Double.toString() method, you would know that the program prints the shortest decimal fraction sufficient to distinguish the double value from its nearest neighbor, with at least one digit before and after the decimal point. The problem is that the number 1.1 can’t be represented exactly as a double, so it is represented by the closest double value. More generally, the problem is that not all decimals can be represented accurately using binary floating-point.

  One of the solutions is to use printf:

System.out.printf("%.2f%n", 2.00 - 1.10);

  Another option would be to make use of BigDecimals:

System.out.println(new BigDecimal("2.00").
subtract(new BigDecimal("1.10")));

  Regardless of what you will choose, you’ll get your 0.90 cents.

Division of large numbers

  Next case! Have you ever had to divide two really big numbers, for instance to divide the amount of microseconds in a day by the amount of milliseconds in a day? I guess not, but imagine there is a task to code that or you are doing it just for fun.

  As far as we have already covered two pitfalls, we are sure that we’ll do it right this time. At least we hope so…

  We write a nice, clean and really straightforward code.

public static void main(String[] args){
     final long MICROS_PER_DAY = 24 * 60 * 60 * 1000 * 1000;
     final long MILLIS_PER_DAY = 24 * 60 * 60 * 1000;
     
     System.out.println(MICROS_PER_DAY / MILLIS_PER_DAY);
}

  The output that we are expecting is 24 hours/day · 60 minutes/hour · 60 seconds/minute · 1,000 milliseconds/second · 1,000 microseconds/millisecond divided by amount of miliseconds. So, we are expecting 1000 but it prints…

>>>> 5

  Again???

  So, what exactly went wrong? The problem is that MICROS PER DAY does overflow. We are multiplying all int values.

  How can we fix it?

public static void main(String[] args){
     final long MICROS_PER_DAY = 24L * 60 * 60 * 1000 * 1000;
     final long MILLIS_PER_DAY = 24L * 60 * 60 * 1000;

     System.out.println(MICROS_PER_DAY / MILLIS_PER_DAY);
}

>>>> 1000

  Yeah, got it now (notice the L next to 24).

String concatenation

  Here’s another situation, tricky but not less interesting – it’s all about concatenation!

  Let’s say we would like to check if the length of two strings is equal (why not?) Okay, now we feel really confident, we know about AT LEAST four Java traps. Let’s do it!

public static void main(String[] args){
     final String firstString= "length: 10";
     final String secondString= "length: " + firstString.length();
     
     System.out.println("Strings are equal: " + firstString == secondString);
}

  What are we expecting?

>>>> Strings are equal: false

  Of course they are not, but wait…

>>>> false

  What’s wrong this time? The + operator, whether used for addition or string concatenation, binds more tightly than the == operator.

  So, the rule for string concatenation is : “always parenthesize non-trivial operands”.

  How to fix it? Check it out!

System.out.println("Strings are equal: " + (firstString == secondString));

>>>> Strings are equal: false

  We did it again!

Working with Integer.MAX_VALUE

  Do you remember what Integer’s max value is? Or maybe something around it? So you are being really careful right now writing a smart piece of code that will find this number, here we go:

public static final int END = Integer.MAX_VALUE;
     public static final int START = END - 10;

     public static void main(String[] args){

     for(int i = START; i <= END; i++)
          System.out.println(i);
}

  And what do we expect? Right – 2147483638, 2147483639, 2147483640 etc.!

  But… it looks like this code won’t stop and will loop forever or until you won’t kill it. What’s the problem? The thing is that the loop continues as long as the loop index i is less than or equal to Integer.MAX_VALUE, but all int variables are always less than or equal to Integer.MAX_VALUE. It is, after all, defined to be the highest int value in Java. When it reaches Integer.MAX_VALUE and performs the incrementation, it silently wraps around to Integer.MIN_VALUE.

  How can we fix it? Take a look:

for(long i = START; i <= END; i++)
  System.out.println(i);

>>>> 2147483638
     2147483639
     2147483640
     2147483641
     2147483642
     2147483643
     2147483644
     2147483645
     2147483646
     2147483647

  Amazing, we are awesome!

  Now you know that you have to be really careful and check your code several times before saying with certainty that it works correctly. Sometimes these little hidden “bugs” might make your program work unexpectedly and cause you trouble later on.

  Hope you enjoyed it!

Share this article:

Alexandru Malina
Java Developer