Search icon
Arrow left icon
All Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Newsletters
Free Learning
Arrow right icon
Java Coding Problems - Second Edition

You're reading from  Java Coding Problems - Second Edition

Product type Book
Published in Mar 2024
Publisher Packt
ISBN-13 9781837633944
Pages 798 pages
Edition 2nd Edition
Languages
Author (1):
Anghel Leonard Anghel Leonard
Profile icon Anghel Leonard

Table of Contents (16) Chapters

Preface Text Blocks, Locales, Numbers, and Math Objects, Immutability, Switch Expressions, and Pattern Matching Working with Date and Time Records and Record Patterns Arrays, Collections, and Data Structures Java I/O: Context-Specific Deserialization Filters Foreign (Function) Memory API Sealed and Hidden Classes Functional Style Programming – Extending APIs Concurrency – Virtual Threads and Structured Concurrency Concurrency ‒ Virtual Threads and Structured Concurrency: Diving Deeper Garbage Collectors and Dynamic CDS Archives Socket API and Simple Web Server Other Books You May Enjoy
Index

Java Coding Problems, Second Edition: Become an expert Java programmer by solving modern real-world problems

Welcome to Packt Early Access. We’re giving you an exclusive preview of this book before it goes on sale. It can take many months to write a book, but our authors have cutting-edge information to share with you today. Early Access gives you an insight into the latest developments by making chapter drafts available. The chapters may be a little rough around the edges right now, but our authors will update them over time.You can dip in and out of this book or follow along from start to finish; Early Access is designed to be flexible. We hope you enjoy getting to know more about the process of writing a Packt book.

  1. Chapter 1: Strings, Locales, Numbers & Math
  2. Chapter 2: Objects, Pattern Matching, and Switch Expressions
  3. Chapter 3: Working with Date and Time
  4. Chapter 4: Record and record pattern
  5. Chapter 5: Arrays, collections and data structures...

Problems

Use the following problems to test your string manipulation, Java locales, and mathematical corner case programming prowess. I strongly encourage you to give each problem a try before you turn to the solutions and download the example programs:

  1. Creating a multiline SQL, JSON, and HTML string: Write a program that declares a multiline string (for instance, SQL, JSON, and HTML strings).
  2. Exemplifying the usage of text block delimiters: Write a program that exemplifies step-by-step how the delimiters of a text block affect the resulting string.
  3. Working with indentation in text blocks: Write a program that exemplifies different techniques to indent a text block. Explain the meaning of incidental and essential white spaces.
  4. Removing incidental white spaces in text blocks: Highlight the main steps of the algorithm used by the compiler to remove the incidental white spaces of a text block.
  5. Using text blocks just for readability: Write a program...

1. Creating a multiline SQL, JSON, and HTML string

Let’s consider the following SQL multiline string:

UPDATE "public"."office"
SET ("address_first", "address_second", "phone") =
  (SELECT "public"."employee"."first_name",
          "public"."employee"."last_name", ?
   FROM "public"."employee"
   WHERE "public"."employee"."job_title" = ?

As is common knowledge, before JDK 8, we could wrap this SQL as a Java String (string literal) in several ways.

Before JDK 8

Probably the most common approach relies on straightforward concatenation via the well-known “+" operator. This way, we obtain a multiline string representation, as follows:

String sql = 
 "UPDATE \"public\".\"office\"\n"
+ "SET (\"address_first\", \"address_second\", \"...

2. Exemplifying the usage of text block delimiters

Remember from the previous problem, Creating a multiline SQL, JSON, and HTML string, that a text block is syntactically delimited by an opening and a closing delimiter, represented by three double quotation marks, """.

The best approach for exemplifying the usage of these delimiters consists of three simple steps: consider an example, inspect the output, and provide a conclusion. This being said, let’s start with an example that imitates some of the JEP’s examples:

String sql= """
            UPDATE "public"."office"
            SET ("address_first", "address_second", "phone") =
              (SELECT "public"."employee"."first_name",
                      "public"."employee"."last_name", ?
               FROM "public"."employee"
               WHERE "...

3. Working with indentation in text blocks

Indentation in text blocks is easy to understand if we have a clear picture of two terms:

  • Incidental (or unessential) white spaces – represent the meaningless white spaces that result from code formatting (leading white spaces commonly added by the IDE) or are added intentionally/accidentally at the end of the text (trailing white spaces)
  • Essential white spaces – represent the white spaces that we explicitly add, which are meaningful for the final string

In Figure 1.3, you can see the incidental versus essential white spaces in a JSON text block:

Figure 1.3.png

Figure 1.3: Incidental versus essential white spaces in a JSON text block

In the left figure, you can see the incidental versus essential white spaces when the closing delimiter is placed at the end of the content. In the middle figure, the closing delimiter was moved to its own line, while in the right figure, we also shifted to the left.

...

4. Removing incidental white spaces in text blocks

Removing incidental white spaces in text blocks is typically a job accomplished by the compiler via a special algorithm. To understand the main aspects of this algorithm, let’s go over it with the following example:

String json = """                     |Compiler:
----{                                 |Line 01: 4  lws
----++"widget": {                     |Line 02: 6  lws
----++++"debug": "on",                |Line 03: 8  lws
----++++"window": {                   |Line 04: 8  lws
----++++++"title": "Sample Widget 1", |Line 05: 10 lws
----++++++"name": "back_window"       |Line 06: 10 lws
----++++},                            |Line 07: 8  lws
----++++"image": {                    |Line 08: 8  lws
----++++++"src": "images\\sw.png"     |Line 09: 10 lws
----++++},                            |Line 10:...

5. Using text blocks just for readability

Using text blocks just for readability can be translated as making a string look like a text block but act as a single-line string literal. This is especially useful for formatting long lines of text. For instance, we may want to have the following SQL string look like a text block (for readability purposes) but act as a single-line string literal (in the sense of being compact when we pass it to the database):

SELECT "public"."employee"."first_name"
FROM "public"."employee" 
WHERE "public"."employee"."job_title" = ?

Starting with JDK 14, we can accomplish this goal via the new escape sequence, \ (a single backslash). By adding this escape sequence at the end of a line, we instruct the compiler to suppress appending a new line character to that line. So in our case, we can express the SQL as a single-line string literal, as follows:

String sql = "...

6. Escaping quotes and line terminators in text blocks

Escaping double quotes is necessary only when we want to embed, in the text block, the sequence of three double quotes ("""), as follows:

String txt = """
             She told me 
                    \"""I have no idea what's going on\""" 
             """;

Escaping """ can be done with \""". There is no need to write \"\"\".

The resulting string will look like this:

She told me
        """I have no idea what's going on"""

Whenever you need to embed " or "", simply do it as follows:

String txt = """
             She told me 
                     "I have no idea what's going on"
             """;
String txt = """
             She told me 
                     ""...

7. Translating escape sequences programmatically

We already know that the compiler is responsible for the translation of escape sequences, and most of the time, there is no need to explicitly interfere in this process. But there are cases when we may need programmatic access to this process (for instance, to explicitly un-escape a string before passing it to a function).

Starting with JDK 15, we can accomplish this via String.translateEscapes(), which is capable of un-escape sequences such as \t, \n, \b, and so on, and octal numbers (\0\377). However, this method doesn’t translate Unicode escapes (\uXXXX).

We can perform an equality test in order to reveal how translateEscapes() works:

String newline = "\\n".translateEscapes();
System.out.println(("\n".equals(newline)) ? "yes" : "no");

As you can already intuit, the result is yes.

Next, let’s assume that we want to use an external service that prints...

8. Formatting text blocks with variables/expressions

In Java, it is a common practice to format string literals with variables/expressions to obtain dynamic strings. For instance, we can create a dynamic piece of XML string via the following well-known concatenation:

String fn = "Jo";
String ln = "Kym";
String str = "<user><firstName>" + fn
  + "</firstName><lastName>" + ln + "</lastName></user>";
// output
<user><firstName>Jo</firstName><lastName>Kym</lastName></user>

Of course, this tiny construction has serious issues from a readability perspective. XML code is human-readable if it is formatted and indented accordingly; otherwise, is really hard to follow its hierarchy. So, can we express this XML to look like the following figure?

Figure 1.6.png

Figure 1.6: Formatted XML

Sure we can! By using some escape sequences (for instance, \n, \t, and \s)...

9. Adding comments in text blocks

Question: Can we add comments in text blocks?

Official answer (according to the Java Language Specification): The lexical grammar implies that comments do not occur within character literals, string literals, or text blocks.

You might be tempted to try something like this, thinking it’s a quick hack, but I really don’t recommend it:

String txt = """
             foo  /* some comment */
             buzz //another comment
             """.replace("some_regex","");

Short answer: No, we cannot have comments in text blocks.

Let’s move on and talk about mixing ordinary string literals with text blocks.

10. Mixing ordinary string literals with text blocks

Before mixing ordinary string literals with text blocks, let’s consider the following statement: How different is an ordinary string literal from a text block? We can answer this question via the following snippet of code:

String str = "I love Java!";
String txt = """
             I love Java!""";
System.out.println(str == txt);      // true
System.out.println(str.equals(txt)); // true

Wow! So our snippet of code prints true twice. This means that an ordinary string literal and a text block are similar at runtime. We can define text blocks as string literals that span across multiple lines of text and use triple quotes as their opening and closing delimiter. How so? First, the instance produced from an ordinary string literal and a text block is of type java.lang.String. Second, we have to look at the compiler internals. Basically, the compiler adds strings to a special cached...

11. Mixing regular expression with text blocks

Regular expressions can be used with text blocks. Let’s consider a simple string, as follows:

String nameAndAddress
  = "Mark Janson;243 West Main St;Louisville;40202;USA";

So here we have a name (Mark Janson) and some details about his address, delimited by a semicolon (;). It is a common scenario to pass such strings through regular expressions and extract the information as named groups. In this example, we can consider five named groups as follows:

  • name: should contain the person’s name (Mark Janson)
  • address: should contain the person’s street information (243 West Main St)
  • city: should contain the person’s city (Louisville)
  • zip: should contain the city’s zip code (40202)
  • country: should contain the country’s name (USA)

A regular expression that can match these named groups may look as follows:

(?<name>[ a-zA-Z]+);(?...

12. Checking if two text blocks are isomorphic

Two text blocks are isomorphic if the resulting strings are isomorphic. Two string literals are considered isomorphic if we can map every character of the first string to every character of the second string in a one-to-one fashion.

For example, consider that the first string is “abbcdd" and the second string is “qwwerr". The one-to-one character mapping is shown in Figure 1.8:

Figure 1.7.png

Figure 1.8: One-to-one character mapping between two strings

So as you can see in Figure 1.8, character “a” of the first string can be replaced by character “q” of the second string. Moreover, character “b” of the first string can be replaced by character “w” of the second string, character “c” by character “e”, and character “d” by character “r”. Obviously, vice versa is also true. In other words, these two strings...

13. Concatenating strings versus StringBuilder

Check out the following plain string concatenation:

String str1 = "I love";
String str2 = "Java";
String str12 = str1 + " " + str2; 

We know that the String class is immutable (a created String cannot be modified). This means that creating str12 requires an intermediary string that represents the concatenation of str1 with white space. So after str12 is created, we know that str1 + " " is just noise or garbage, since we cannot refer to it further.

In such scenarios, the recommendation is to use a StringBuilder, since it is a mutable class and we can append strings to it. So this is how the following statement was born: In Java, don’t use the “+" operator to concatenate strings! Use StringBuilder, which is much faster.

Have you heard this statement before? I’m pretty sure you have, especially if you still run your applications on JDK 8 or even on a previous...

14. Converting int to String

As usual, in Java, we can accomplish a task in multiple ways. For instance, we can convert an int (primitive integer) to String via Integer.toString(), as follows:

public String intToStringV1(int v) {
  return Integer.toString(v);
}

Alternatively, you can accomplish this task via a quite common hack (the code reviewer will raise his eyebrow here), consisting of concatenating an empty string with the integer:

public String intToStringV2(int v) {
  return "" + v;
}

String.valueOf() can also be used as follows:

public String intToStringV3(int v) {
  return String.valueOf(v);
}

A more esoteric approach via String.format() is as follows:

public String intToStringV4(int v) {
  return String.format("%d", v);
}

These methods also work for a boxed integer and, therefore, for an Integer object. Since boxing and unboxing are costly operations, we strive to avoid them unless they are really necessary. However...

15. Introducing string templates

Until JDK 21, Java allows us to perform string composition for SQL, JSON, XML, and so on via different approaches, covered earlier in Problem 8. In that problem, you can see how to use text blocks and embedded expressions via simple concatenation, using the plus (+) operator, StringBuilder.append(), String.format(), formatted(), and so on. While using the plus (+) operator and StringBuilder.append() can be cumbersome and affect readability, the String.format() and formatted() may cause type mismatches. For instance, in the following example, it is quite easy to mess up the data types (LocalDate, double, and String) and the format specifiers (%d, %s, and %.2f):

LocalDate fiscalDate = LocalDate.now(); 
double value = 4552.2367; 
String employeeCode = "RN4555"; 
 
String jsonBlock = """ 
                 {"sale": { 
                     "id": 1, 
                     "details": { 
          ...

16. Writing a custom template processor

The built-in STR and FMT can return only String instances and cannot throw exceptions. However, both of them are actually instances of the functional interface StringTemplate.Processor<R,E extends Throwable>, which defines the process() method:

R process(StringTemplate stringTemplate) throws E 

By implementing the Processor<R,E extends Throwable> interface, we can write custom template processors that return R (any result type), not just String. Moreover, if something goes wrong during processing (for instance, validation issues are present), we can throw checked exceptions (E extends Throwable).

For instance, let’s assume that we need to interpolate strings with expressions representing phone numbers. So we accept only the expressions that are phone numbers, matching the following regular expression:

private static final Pattern PHONE_PATTERN = Pattern.compile( 
  "\\d{10}|(?:\\d{3}-){2}\\d{4}|\\(\\d...

17. Creating a Locale

A Java Locale (java.util.Locale) represents an object that wraps information about a specific geographical, political, or cultural region – that is, an object useful for internationalization purposes. A Locale is typically used in conjunction with DateFormat/DateTimeFormatter to represent date-time in a format specific to a country, with NumberFormat (or its subclass, DecimalFormat) used to represent numbers in a format specific to a country (for instance, to represent an amount of money in a specific currency), or with MessageFormat to create formatted messages for a specific country.

For the most popular locales, Java provides a list of constants (for instance, Locale.GERMANY, Locale.CANADA, and so on). For locales that are not on this list, we have to use the formats defined in several RFCs. Most commonly, we use the language pattern (for instance, ro for Romanian) or the language_country pattern (for instance, ro_RO for Romania, en_US for United...

18. Customizing localized date-time formats

Starting with JDK 8, we have a comprehensive date-time API containing classes such as LocalDate, LocalTime, LocalDateTime, ZonedDateTime, OffsetDateTime, and OffsetTime.

We can easily format the date-time output returned by these classes via DateTimeFormatter.ofPattern(). For instance, here, we format a LocalDateTime via the y-MM-dd HH:mm:ss pattern:

// 2023-01-07 15:31:22
String ldt = LocalDateTime.now()
  .format(DateTimeFormatter.ofPattern("y-MM-dd HH:mm:ss"));

More examples are available in the bundled code.

How about customizing our format based on a given locale – for instance, Germany?

Locale.setDefault(Locale.GERMANY);

We accomplish this via ofLocalizedDate(),ofLocalizedTime(), and ofLocalizedDateTime(), as in the following examples:

// 7. Januar 2023
String ld = LocalDate.now().format(
  DateTimeFormatter.ofLocalizedDate(FormatStyle.LONG));
// 15:49
String lt = LocalTime.now().format...

19. Restoring Always-Strict Floating-Point semantics

Floating-point calculations are not easy! Even some simple arithmetical properties don’t apply to such calculations. For instance, floating-point addition or multiplication is not associative. In other words (x + y) + z is not equal to x + (y + z) where x, y, and z are real numbers. A quick example to test the associativity of multiplication follows:

double x = 0.8793331;
double y = 12.22933;
double z = 901.98334884433;
double m1 = (x * y) * z;   // 9699.617442382583 
double m2 = (x * (y * z)); // 9699.617442382581
// m1 == m2 returns false

This means that floating-point arithmetic is a methodical approximation of real arithmetic. Computers have to approximate because of some limitations. For instance, exact floating-point outputs become very large quite quickly. Moreover, the exact inputs are not known, so with inexact inputs, it is difficult to obtain exact outputs.

To solve this problem, Java has to adopt...

20. Computing mathematical absolute value for int/long and result overflow

Mathematical absolute value is notated by placing the value between two pipe operators and is computed as follows:

|x| = x, |-x| = x

It is commonly used for computing/expressing distances. For example, imagine that 0 represents the sea level and we have a scuba diver and a climber. The scuba diver is underwater at -45 ft (notice that we use negative numbers to express how deep in the water the scuba diver is). At the same time, the climber has climbed 30 ft high. Which of them is closer to the sea level (0)? We may think that since -45 < 30, the scuba diver is closer because its value is smaller. However, we can easily find the correct answer by applying the mathematical absolute, as follows:

|-45| = 45, |30| = 30
45 > 30, so the climber is closer to the sea level (0)

Now, let’s dive into the solution with the following example:

int x = -3;
int absofx = Math.abs(x); // 3
...

21. Computing the quotient of the arguments and result overflow

Let’s start with two simple computations, as follows:

-4/-1 = 4, 4/-1 = -4

This is a very simple use case that works as expected. Now, let’s keep the divisor as -1, and let’s change the dividend to Integer.MIN_VALUE (-2,147,483,648):

int x = Integer.MIN_VALUE;
int quotient = x/-1; // -2,147,483,648

This time, the result is not correct. The int domain was overflowed because of |Integer.MIN_VALUE| > |Integer.MAX_VALUE|. It should be the positive 2,147,483,648, which doesn’t fit in the int domain. However, changing the x type from int to long will solve the problem:

long x = Integer.MIN_VALUE;
long quotient = x/-1; // 2,147,483,648

But the problem will reappear if, instead of Integer.MIN_VALUE, there is Long.MIN_VALUE:

long y = Long.MIN_VALUE; // -9,223,372,036,854,775,808
long quotient = y/-1;    // -9,223,372,036,854,775,808

Starting with JDK 18, the Math...

22. Computing the largest/smallest value that is less/greater than or equal to the algebraic quotient

By the largest value, we understand the value closest to positive infinity, while by the smallest value, we understand the value closest to negative infinity.

Computing the largest value that is less than or equal to the algebraic quotient can be done, starting with JDK 8, via floorDiv(int x, int y) and floorDiv(long x, long y). Starting with JDK 9, we also have floorDiv(long x, int y).

Computing the smallest value that is greater than or equal to the algebraic quotient can be done, starting with JDK 18, via ceilDiv(int x, int y), ceilDiv(long x, int y), and ceilDiv(long x, long y).

However, none of these functions are capable of managing the corner case divisions presented in the previous problem, Integer.MIN_VALUE/-1 and Long.MIN_VALUE/-1:

int x = Integer.MIN_VALUE; // or, x = Long.MIN_VALUE
Math.floorDiv(x, -1); // -2,147,483,648
Math.ceilDiv(x, -1);  // -2,147...

23. Getting integral and fractional parts from a double

You know those problems that are very easy if you know the solution and seem very difficult if you don’t? This is exactly that kind of a problem. The solution is quite simple, as you can see in the following code:

double value = -9.33543545;
double fractionalPart = value % 1;
double integralPart = value - fractionalPart;

This was easy; I don’t think you need further explanations. But this approach is not quite accurate. I mean, the integral part is -9, but this returns -9.0. Also, the fractional part is -0.33543545, but the returned value is -0.3354354500000003.

If we need a more accurate result, then using BigDecimal is more useful:

BigDecimal bd = BigDecimal.valueOf(value);
int integralPart = bd.intValue();
double fractionalPart = bd.subtract(
       BigDecimal.valueOf(integralPart)).doubleValue();

This time, the results are -9 and -0.33543545.

24. Testing if a double number is an integer

First of all, let’s consider the following expected results (false means that the double is not an integer):

double v1 = 23.11;                    // false
double v2 = 23;                       // true
double v3 = 23.0;                     // true
double v4 = Double.NaN;               // false
double v5 = Double.NEGATIVE_INFINITY; // false
double v6 = Double.POSITIVE_INFINITY; // false

Most probably, the first solution for testing if a double number is an integer consists of a simple cast as follows:

public static boolean isDoubleIntegerV1(double v) {
  return v == (int) v;
}

However, there are several other options. For instance, we can rely on modulus, as follows:

public static boolean isDoubleIntegerV2(double v) {
  return v % 1 == 0;
}

Alternatively, we can rely on the Math.floor() and Double.isFinite() methods. If the given double is a finite number and is equal to the result of Math.floor(), then...

25. Hooking Java (un)signed integers in a nutshell

Signed values (or variables) such as signed integers or signed longs allow us to represent negative and positive numbers.

Unsigned values (or variables) such as unsigned integers or unsigned longs allow us to represent only positive numbers.

Signed and unsigned values (variables) of the same type share the same range. However, as you can see in the following figure, unsigned variables cover a larger magnitude number.

Figure 1.20.png

Figure 1.20: Signed and unsigned integers

The signed 32-bit integers range from –2,147,483,648 to 2,147,483,647 (around 4 billion values). Unsigned 32-bit integers range from 0 to 4,294,967,295 (also around 4 billion values).

So when we use signed integer variables, we can use 2 billion positive values, but when we use unsigned integer variables, we can use 4 billion positive values. The hatched part of the figure represents the extra 2 billion positive integer values.

Commonly, unsigned...

26. Returning the flooring/ceiling modulus

Having the dividend / divisor = quotient computation, we know that the floor operation applied to the (dividend, divisor) pair returns the largest integer that is less than or equal to the algebraic quotient. By the largest integer, we understand the integer closest to positive infinity. Starting with JDK 8, this operation can be obtained via Math.floorDiv() and, starting with JDK 18, Math.floorDivExact().

On the other hand, the ceil operation applied to the (dividend, divisor) pair returns the smallest integer that is greater than or equal to the algebraic quotient. By the smallest integer, we understand the integer closest to negative infinity. Starting with JDK 18, this operation can be obtained via Math.ceilDiv() and Math.ceilDivExact().

More details are available in Problem 22.

Now, based on the floor and ceil operations, we can define the following floor/ceil modulus relationships:

Floor_Modulus = dividend -
  (floorDiv...

27. Collecting all prime factors of a given number

A prime number is a number divisible by itself and 1 (for instance, 2, 3, and 5 are prime numbers). Having a given number, we can extract its prime factors, as in the following figure:

Figure 1.22.png

Figure 1.22: Prime factors of 90 are 2, 3, 3, and 5

The prime factors of 90 are 2, 3, 3, and 5. Based on Figure 1.22, we can create an algorithm to solve this problem, as follows:

  1. Define a List to collect prime factors of the given v.
  2. Initialize a variable s with 2 (the smallest prime number).
  3. If v % s is 0, collect s as a prime factor and compute the new v as v / s.
  4. If v % s is not 0, then increase s by 1.
  5. Repeat step 3 as long as v is greater than 1.

In code lines, this O(n) algorithm (O(log n) for composite numbers) can be expressed as follows:

public static List<Integer> factors(int v) {
  List<Integer> factorsList = new ArrayList<>();
  int s = 2;
  while (v > 1)...

28. Computing the square root of a number using the Babylonian method

Believe it or not, the ancient Babylonians (around 1500 BC) knew how to estimate square roots long before the popular method discovered by Newton.

Mathematically speaking, the Babylonian approach for estimating the square root of v > 0 is the recurrence relation from the following figure:

Figure 1.23.png

Figure 1.23: The recurrence relation of Babylonian square root approximation

The recurrence formula starts with an initial guess of x0. Next, we calculate x1, x2, …, xn by substituting xn-1 in the formula on the right-hand side and evaluating the expression.

For instance, let’s try to apply this formula to estimate the square root of 65 (the result is 8.06). Let’s start with x0 as 65/2, so x0 =32.5, and let’s calculate x1 as:

Having x1, we can calculate x2 as follows:

Having x2, we can calculate x3 as follows:

We are getting closer to the final result...

29. Rounding a float number to specified decimals

Consider the following float number and the number of decimals that we want to keep:

float v = 14.9877655f;
int d = 5;

So the expected result after rounding up is 14.98777.

We can solve this problem in a straightforward manner in at least three ways. For instance, we can rely on the BigDecimal API, as follows:

public static float roundToDecimals(float v, int decimals) {
  BigDecimal bd = new BigDecimal(Float.toString(v));
  bd = bd.setScale(decimals, RoundingMode.HALF_UP);
  return bd.floatValue();
}

First, we create a BigDecimal number from the given float. Second, we scale this BigDecimal to the desired number of decimals. Finally, we return the new float value.

Another approach can rely on DecimalFormat, as follows:

public static float roundToDecimals(float v, int decimals) {
  DecimalFormat df = new DecimalFormat();
  df.setMaximumFractionDigits(decimals);
  return Float.parseFloat(df.format(v));
...

30. Clamping a value between min and max

Let’s assume that we have a pressure regulator that is capable of adjusting the given pressure in a certain range. For instance, if the passed pressure is below the minimum pressure, then the regulator increases the pressure to the minimum pressure. On the other hand, if the passed pressure is higher than the maximum pressure, then the regulator decreases the pressure to the maximum pressure. Moreover, if the passed pressure is between the minimum (inclusive) and maximum (inclusive) pressure, then nothing happens – this is the normal pressure.

Coding this scenario can be done in a straightforward manner, as follows:

private static final int MIN_PRESSURE = 10; 
private static final int MAX_PRESSURE = 50; 
 
public static int adjust(int pressure) { 
         
  if (pressure < MIN_PRESSURE) { 
    return MIN_PRESSURE; 
  } 
         
  if (pressure > MAX_PRESSURE) { 
    return MAX_PRESSURE; 
  } 
         
  return...

31. Multiply two integers without using loops, multiplication, bitwise, division, and operators

The solution to this problem can start from the following algebraic formula, also known as the special binomial product formula:

Figure 1.24.png

Figure 1.24: Extracting a*b from a binomial formula

Now that we have the a*b product, there is only one issue left. The formula of a*b contains a division by 2, and we are not allowed to explicitly use the division operation. However, the division operation can be mocked in a recursive fashion, as follows:

private static int divideByTwo(int d) {
  if (d < 2) {
    return 0;
  }
  return 1 + divideByTwo(d - 2);
}

Nothing can stop us now from using this recursive code to implement a*b, as follows:

public static int multiply(int p, int q) {
  // p * 0 = 0, 0 * q = 0
  if (p == 0 || q == 0) {
    return 0;
  }
  int pqSquare = (int) Math.pow(p + q, 2);
  int pSquare = (int) Math.pow(p, 2);
  int qSquare = (int) Math.pow(q, 2);
  int squareResult...

32. Using TAU

What is TAU?

Short answer: It is the Greek letter .

Long answer: It is a Greek letter used to define the proportion of the circumference of a circle to its radius. Put simply, TAU is one turn of an entire circle, so 2*PI.

TAU allows us to express sinuses, cosines, and angles in a more intuitive and simple way. For instance, the well-known angles of 300, 450, 900, and so on can be easily expressed in radians via TAU as a fraction of a circle, as in the following figure:

Figure 1.25.png

Figure 1.25: Angles represented using TAU

This is more intuitive than PI. It is like slicing a pie into equal parts. For instance, if we slice at TAU/8 (450), it means that we sliced the pie into eight equal parts. If we slice at TAU/4 (900), it means that we sliced the pie into four equal parts.

The value of TAU is 6.283185307179586 = 2 * 3.141592653589793. So the relationship between TAU and PI is TAU=2*PI. In Java, the well-known PI is represented via the Math.PI constant...

33. Selecting a pseudo-random number generator

When we flip a coin or roll the dice, we say that we see “true” or “natural” randomness at work. Even so, there are tools that pretend they are capable of predicting the path of flipping a coin, rolling dice, or spinning a roulette wheel, especially if some contextual conditions are met.

Computers can generate random numbers using algorithms via the so-called random generators. Since algorithms are involved, the generated numbers are considered pseudo-random. This is known as “pseudo”-randomness. Obviously, pseudo-random numbers are also predictable. How so?

A pseudo-random generator starts its job by seeding data. This is the generator’s secret (the seed), and it represents a piece of data used as the starting point to generate pseudo-random numbers. If we know how the algorithm works and what the seed was, then the output is predictable. Without knowing the seed, the rate of predictability...

34. Filling a long array with pseudo-random numbers

When we want to fill up a large array with data, we can consider the Arrays.setAll() and Arrays.parallelSetAll(). These methods can fill up an array by applying a generator function to compute each element of the array.

Since we have to fill up the array with pseudo-random data, we should consider that the generator function should be a pseudo-random generator. If we want to do this in parallel, then we should consider the SplittableRandom (JDK 8+)/SplittableGenerator (JDK 17+), which are dedicated to generating pseudo-random numbers in isolated parallel computations. In conclusion, the code may look as follows (JDK 17+):

SplittableGenerator splittableRndL64X256 
  = RandomGeneratorFactory
     .<SplittableGenerator>of("L64X256MixRandom").create();
long[] arr = new long[100_000_000];
Arrays.parallelSetAll(arr, 
                      x ->splittableRndL64X256.nextLong());

Alternatively, we can use SplittableRandom...

35. Creating a stream of pseudo-random generators

Before creating a stream of pseudo-random generators, let’s create a stream of pseudo-random numbers. First thing first, let’s see how to do it with the legacy Random, SecureRandom, and ThreadLocalRandom.

Since these three pseudo-random generators contain methods such as ints() returning IntStream, doubles() returning DoubleStream, and so on, we can easily generate an (in)finite stream of pseudo-random numbers, as follows:

Random rnd = new Random();
// the ints() flavor returns an infinite stream
int[] arrOfInts = rnd.ints(10).toArray(); // stream of 10 ints
// or, shortly
int[] arrOfInts = new Random().ints(10).toArray();

In our examples, we collect the generated pseudo-random numbers in an array. Of course, you can process them as you want. We can obtain similar results via SecureRandom, as follows:

SecureRandom secureRnd = SecureRandom.getInstanceStrong();
int[] arrOfSecInts = secureRnd.ints(10).toArray...

36. Getting a legacy pseudo-random generator from new ones of JDK 17

A legacy pseudo-random generator such as Random, SecureRandom, or ThreadLocalRandom can delegate method calls to a RandomGenerator, passed as an argument to Random.from(), SecureRandom.from(), or ThreadLocalRandom.from(), as follows:

Random legacyRnd = Random.from(
   RandomGenerator.of("L128X256MixRandom"));
// or, like his
Random legacyRnd = Random.from(RandomGeneratorFactory.
   of("Xoroshiro128PlusPlus").create());
// or, like this
Random legacyRnd = Random.from(RandomGeneratorFactory
   .<RandomGenerator.SplittableGenerator>of(
      "L128X256MixRandom").create());

The from() methods are available starting with JDK 19. In the bundled code, you can see more examples.

37. Using pseudo-random generators in a thread-safe fashion (multithreaded environments)

Random and SecureRandom instances are thread-safe. While this statement is true, pay attention that when a Random instance (or Math.random()) is used by multiple threads (multithreaded environment), your code is prone to thread contention because these threads share the same seed. Sharing the same seed involves synchronization of the seed access; therefore, it opens the door to thread contention. Obviously, thread contention leads to performance penalties, since threads may wait in the queue to gain access to the seed. Synchronization is typically expensive.

An alternative to Random is ThreadLocalRandom, which uses a Random instance for each thread and provides protection against thread contention, since it doesn’t contain synchronized code or atomic operations. The downside is that ThreadLocalRandom uses an internal seed per thread that we cannot control or modify.

SplittableRandom...

Summary

This chapter collected 37 problems related to strings, locales, numbers, and math, intended to mix classical must-know problems with a bunch of problems solved via the latest JDK features, such as text blocks and pseudo-random generators. If you want to explore other similar problems, then consider Java Coding Problems, First Edition, which has a similar chapter (Chapter 1), covering another 39 problems.

Leave a review!

Enjoying this book? Help readers like you by leaving an Amazon review. Scan the QR code below for a 20% discount code.

*Limited Offer

lock icon The rest of the chapter is locked
You have been reading a chapter from
Java Coding Problems - Second Edition
Published in: Mar 2024 Publisher: Packt ISBN-13: 9781837633944
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $15.99/month. Cancel anytime}