Posts

Showing posts with the label Puzzles

A Functional intreface can extend other interfaces

Image
A functional interface can extend a marker interface, functional interface, non-functional interface, and an annotation. A recent X poll suggested 25% of those responding didn't know that.

A Java Conversion Puzzler: Understanding Implicit Casting and Overflow

This article explores a subtle Java conversion puzzle that challenges assumptions about how arithmetic operations, implicit casting, and floating-point conversions interact. Inspired by complexities often encountered in low-latency and high-performance environments, it demonstrates why a keen understanding of Java’s type system is essential for building reliable and efficient applications. Introduction The following example demonstrates a scenario where an innocuous-looking arithmetic operation leads to a surprising result. While such questions are rare and arguably impractical, they highlight subtle behaviours that can affect correctness and performance, especially in critical systems like high-frequency trading platforms or complex data-processing pipelines. The Problem: A Surprising Print Statement Consider the following code: int i = Integer.MAX_VALUE; i += 0.0f; int j = i; System.out.println(j == Integer.MAX_VALUE); // true At first glance, one might assume that adding...

Why Does Math.round(0.49999999999999994) Round to 1?

1. Defining the Problem In many numerical computations, one would reasonably expect that rounding 0.499999999999999917 should yield 0 , since it appears to be slightly less than 0.5 . Yet, in Java 6, calling Math.round() on this value returns 1 , a result that may initially seem baffling. This seemingly minor discrepancy stems from the interplay of binary floating-point representation, rounding modes, and the particular internal implementation details of Math.round() in earlier Java releases. For professionals in performance-sensitive environments—such as those working in financial technology or high-precision scientific applications—understanding these subtleties is more than just an academic exercise. Even tiny rounding differences can influence trading algorithms, pricing models, or simulations. Moreover, developers and enthusiasts who appreciate the low-level mechanics behind Java’s numeric types will find valuable insights into how these internal workings affect everyday pro...

What can make Java code go faster, and then slower?

It's well-known that the JVM optimises code during execution, resulting in faster performance over time. However, less commonly understood is how operations performed before a code section can negatively impact its execution speed. In this post, I'll use practical examples to explore how warming up and cooling down code affects performance. The code is available here for you to run Warming Up Code When code is executed repeatedly, the JVM optimises performance. Consider the following code snippet: int[] display = {0, 1, 10, 100, 1_000, 10_000, 20_000, 100_001}; for (int i = 0; i <= display[display.length - 1]; i++) { long start = System.nanoTime(); doTask(); long time = System.nanoTime() - start; if (Arrays.binarySearch(display, i) >= 0) System.out.printf("%,d: Took %,d us to serialise/deserialise GregorianCalendar%n", i, time / 1_000); } This code measures the time taken to execute doTask() over multiple iterations, printing...

Unexpected Full GCs Triggered by RMI in Latency-Sensitive Applications

We observed an unexpected increase in Full Garbage Collections (Full GCs) while optimising a latency-sensitive application with minimal object creation. Despite reducing the frequency of minor GCs to enhance performance, the system began to exhibit hourly periodic pauses due to Full GCs, which was counterintuitive. Investigating the Source of Full GCs Upon closer examination, we discovered that the Java Remote Method Invocation (RMI) system was initiating Full GCs every hour. Specifically, the RMI Distributed Garbage Collector (DGC) checks if a GC has occurred in the last hour and, if not, forces a Full GC. This behaviour occurs even if the application does not actively use RMI, leading to unnecessary performance overhead. Understanding RMI's Impact on Garbage Collection The RMI DGC collects periodic garbage to clean up unused remote objects. By default, it is configured to trigger a Full GC if none has occurred within a specified interval (defaulting to one hour). This me...

Calculating an Average Without Overflow: Rounding Methods

Calculating the midpoint between two integers may seem trivial, but the naive approach can lead to overflow errors. Code sample MidpointCalculator is available here: Code sample MidpointCalculator . The classic midpoint formula: int m = ( h + l ) / 2 ; This is prone to overflow if h and l are large, causing the result to be incorrect. This bug appears in many algorithms, including binary search implementations. Understanding the Problem of Overflow In Java, the int type has a fixed range from -2,147,483,648 to 2,147,483,647 . If h and l are large, their sum might exceed this range, leading to overflow. When overflow occurs, Java wraps around the result to the negative range without warning, causing unpredictable results. Safer Approaches to Calculate a Midpoint Several alternative methods can be employed to circumvent the overflow issue. Below, we discuss three approaches, each with merits and use cases. 1. Using a Safer Formula A well-...

Incomparable Puzzles in Java

Here are a few puzzles for you to solve in Java. The source is available here: UncomparablePuzzles.java . Puzzle 1: Comparing long and double Try running the following code to reproduce the output below. See if you can work out why these results occur: long a = ( 1L << 54 ) + 1 ; double b = a ; System . out . println ( "b == a is " + ( b == a )); System . out . println ( "(long) b < a is " + (( long ) b < a )); When executed, it produces: b == a is true (long) b < a is true Analysis This puzzle highlights the precision limitations when converting between long and double . Precision Loss During Conversion The long value a is (1L << 54) + 1 , which is 18014398509481985 . When cast to double , b becomes 18014398509481984.0 . Due to double 's 53-bit mantissa, it cannot accurately represent every long value beyond this range, resulting in precision loss. Equality Comparison ( b ...

Java Arrays, Wat!

Java arrays are a fundamental component of the language, yet they can exhibit behaviours that surprise even seasoned developers. This article delves into some of these quirks, providing clarity and practical insights for Java developers. Is it an Array or Not? Consider the following declaration: Serializable [] array2 = new Serializable [ 9 ]; Serializable array = array ; Cloneable [][] arrayA = new Serializable [ 9 ][]; Cloneable [] arrayB = arrayA ; Cloneable arrayC = arrayA ; At first glance, one might question whether array is an array or a scalar. In reality, array is a scalar reference that points to an array. This behaviour is consistent across Java’s type system. For instance: Object o = new Object [ 9 ]; Here, you can assign an array to an Object variable because arrays are also objects in Java. Additionally, arrays are Serializable and Cloneable , so they can be assigned to a Serializable or Cloneable reference...

Some Common Java Gotchas and How to Avoid Them

Advanced Java Questions These questions delve into Java’s more intricate behaviours and are often too advanced for typical interviews, as they might be discouraging for candidates. However, they are excellent for deepening your understanding of Java’s core workings in your own time. Myth 1: System.exit(0) Prevents finally Block Execution Consider the following code: System . setSecurityManager ( new SecurityManager () { @Override public void checkExit ( int status ) { throw new ThreadDeath (); } }); try { System . exit ( 0 ); } finally { System . out . println ( "In the finally block" ); } This code will output: In the finally block Explanation : The System.exit(0) call triggers the checkExit method in the custom SecurityManager . By throwing a ThreadDeath exception instead of terminating, the finally block is allowed to execute, explaining the "In the finally block" output. Since Th...