Book Image

Java Coding Problems - Second Edition

By : Anghel Leonard
Book Image

Java Coding Problems - Second Edition

By: Anghel Leonard

Overview of this book

The super-fast evolution of the JDK between versions 12 and 21 has made the learning curve of modern Java steeper, and increased the time needed to learn it. This book will make your learning journey quicker and increase your willingness to try Java’s new features by explaining the correct practices and decisions related to complexity, performance, readability, and more. Java Coding Problems takes you through Java’s latest features but doesn’t always advocate the use of new solutions — instead, it focuses on revealing the trade-offs involved in deciding what the best solution is for a certain problem. There are more than two hundred brand new and carefully selected problems in this second edition, chosen to highlight and cover the core everyday challenges of a Java programmer. Apart from providing a comprehensive compendium of problem solutions based on real-world examples, this book will also give you the confidence to answer questions relating to matching particular streams and methods to various problems. By the end of this book you will have gained a strong understanding of Java’s new features and have the confidence to develop and choose the right solutions to your problems.
Table of Contents (16 chapters)
1
Text Blocks, Locales, Numbers, and Math
Free Chapter
2
Objects, Immutability, Switch Expressions, and Pattern Matching
14
Other Books You May Enjoy
15
Index

44. Converting between bytes and hex-encoded strings

Converting bytes to hexadecimal (and vice versa) is a common operation in applications that manipulate fluxes of files/messages, perform encoding/decoding tasks, process images, and so on.

A Java byte is a number in the [-128, +127] range and is represented using 1 signed byte (8 bits). A hexadecimal (base 16) is a system based on 16 digits (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, and F). In other words, those 8 bits of a byte value accommodate exactly 2 hexadecimal characters in the range 00 to FF. The decimal <-> binary <-> hexadecimal mapping is resumed in the following figure:

Figure 2.27.png

Figure 2.27: Decimal to binary to hexadecimal conversion

For instance, 122 in binary is 01111010. Since 0111 is in hexadecimal 7, and 1010 is A, this results in 122 being 7A in hexadecimal (also written as 0x7A).

How about a negative byte? We know from the previous chapter that Java represents a negative number as two’s complement of the positive number. This means that -122 in binary is 10000110 (retain the first 7 bits of positive 122 = 1111010, flip(1111010) = 0000101, add(0000001) = 00000110, and append sign bit 1, 10000110) and in hexadecimal, is 0x86.

Converting a negative number to hexadecimal can be done in several ways, but we can easily obtain the lower 4 bits as 10000110 & 0xF = 0110, and the higher four bits as (10000110>> 4) & 0xF = 1000 & 0xF = 1000 (here, the 0xF (binary, 1111) mask is useful only for negative numbers). Since, 0110 = 6 and 1000 = 8, we see that 10000110 is in hexadecimal 0x86.

If you need a deep coverage of bits manipulation in Java or you simply face issues in understanding the current topic, then please consider the book The Complete Coding Interview Guide in Java, especially Chapter 9.

So, in code lines, we can rely on this simple algorithm and Character.forDigit(int d, int r), which returns the character representation for the given digit (d) in the given radix (r):

public static String byteToHexString(byte v) {
  int higher = (v >> 4) & 0xF;
  int lower = v & 0xF;
  String result = String.valueOf(
    new char[]{
      Character.forDigit(higher, 16),
      Character.forDigit(lower, 16)}
    );
  return result;
}

There are many other ways to solve this problem (in the bundled code, you can see another flavor of this solution). For example, if we know that the Integer.toHexString(int n) method returns a string that represents the unsigned integer in base 16 of the given argument, then all we need is to apply the 0xFF (binary, 11111111) mask for negatives as:

public static String byteToHexString(byte v) {
  return Integer.toHexString(v & 0xFF);
}

If there is an approach that we should avoid, then that is the one based on String.format(). The String.format("%02x ", byte_nr) approach is concise but very slow!

How about the reverse process? Converting a given hexadecimal string (for instance, 7d, 09, and so on) to a byte is quite easy. Just take the first (d1) and second (d2) character of the given string and apply the relation, (byte) ((d1 << 4) + d2):

public static byte hexToByte(String s) {
  int d1 = Character.digit(s.charAt(0), 16);
  int d2 = Character.digit(s.charAt(1), 16);
  return (byte) ((d1 << 4) + d2);
} 

More examples are available in the bundled code. If you rely on third-party libraries, then check Apache Commons Codec (Hex.encodeHexString()), Guava (BaseEncoding), Spring Security (Hex.encode()), Bouncy Castle (Hex.toHexString()), and so on.

JDK 17+

Starting with JDK 17, we can use the java.util.HexFormat class. This class has plenty of static methods for handling hexadecimal numbers, including String toHexDigits(byte value) and byte[]parseHex(CharSequence string). So, we can convert a byte to a hexadecimal string as follows:

public static String byteToHexString(byte v) {
  HexFormat hex = HexFormat.of();
  return hex.toHexDigits(v);
}

And, vice versa as follows:

public static byte hexToByte(String s) {
  HexFormat hex = HexFormat.of();
  return hex.parseHex(s)[0];
}

In the bundled code, you can also see the extrapolation of these solutions for converting an array of bytes (byte[]) to a String, and vice versa.