Performance question: Fastest way to convert hexadecimal char to its number value in Java?
I want to convert from char representing a hexadecimal value (in upper or lower case) to byte, like
'0'->0, '1' -> 1, 'A' -> 10, 'a' -> 10, 'f' -> 15 etc...
I will be calling this method extremely often, so performance is important. Is there a faster way than to use a pre-initialized HashMap<Character,Byte>
to get the value from?
Answer
It seems like it's a tossup between using a switch-case and Jon Skeet's direct computing solution - the switch-case solution seems to edge out ever so slightly, though. Greg's array method wins out. Here are the performance results (in ms) for 200,000,000 runs of the various methods:
Character.getNumericValue:
8360
Character.digit:
8453
HashMap<Character,Byte>:
15109
Greg's Array Method:
6656
JonSkeet's Direct Method:
7344
Switch:
7281
Thanks guys!
Benchmark method code
Here ya go, JonSkeet, you old competitor. ;-)
public class ScratchPad {
private static final int NUMBER_OF_RUNS = 200000000;
static byte res;
static HashMap<Character, Byte> map = new HashMap<Character, Byte>() {{
put( Character.valueOf( '0' ), Byte.valueOf( (byte )0 ));
put( Character.valueOf( '1' ), Byte.valueOf( (byte )1 ));
put( Character.valueOf( '2' ), Byte.valueOf( (byte )2 ));
put( Character.valueOf( '3' ), Byte.valueOf( (byte )3 ));
put( Character.valueOf( '4' ), Byte.valueOf( (byte )4 ));
put( Character.valueOf( '5' ), Byte.valueOf( (byte )5 ));
put( Character.valueOf( '6' ), Byte.valueOf( (byte )6 ));
put( Character.valueOf( '7' ), Byte.valueOf( (byte )7 ));
put( Character.valueOf( '8' ), Byte.valueOf( (byte )8 ));
put( Character.valueOf( '9' ), Byte.valueOf( (byte )9 ));
put( Character.valueOf( 'a' ), Byte.valueOf( (byte )10 ));
put( Character.valueOf( 'b' ), Byte.valueOf( (byte )11 ));
put( Character.valueOf( 'c' ), Byte.valueOf( (byte )12 ));
put( Character.valueOf( 'd' ), Byte.valueOf( (byte )13 ));
put( Character.valueOf( 'e' ), Byte.valueOf( (byte )14 ));
put( Character.valueOf( 'f' ), Byte.valueOf( (byte )15 ));
put( Character.valueOf( 'A' ), Byte.valueOf( (byte )10 ));
put( Character.valueOf( 'B' ), Byte.valueOf( (byte )11 ));
put( Character.valueOf( 'C' ), Byte.valueOf( (byte )12 ));
put( Character.valueOf( 'D' ), Byte.valueOf( (byte )13 ));
put( Character.valueOf( 'E' ), Byte.valueOf( (byte )14 ));
put( Character.valueOf( 'F' ), Byte.valueOf( (byte )15 ));
}};
static int[] charValues = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, -1, 10, 11, 12, 13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,10, 11, 12, 13,14,15};
static char[] cs = new char[]{'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f','A','B','C','D','E','F'};
public static void main(String args[]) throws Exception {
long time = System.currentTimeMillis();
for( int i = 0; i < NUMBER_OF_RUNS; i++ ) {
res = getNumericValue( i );
}
System.out.println( "Character.getNumericValue:" );
System.out.println( System.currentTimeMillis()-time );
time = System.currentTimeMillis();
for( int i = 0; i < NUMBER_OF_RUNS; i++ ) {
res = getDigit( i );
}
System.out.println( "Character.digit:" );
System.out.println( System.currentTimeMillis()-time );
time = System.currentTimeMillis();
for( int i = 0; i < NUMBER_OF_RUNS; i++ ) {
try {
res = getValueFromArray( i );
} catch (IllegalArgumentException e) {
}
}
System.out.println( "Array:" );
System.out.println( System.currentTimeMillis()-time );
time = System.currentTimeMillis();
for( int i = 0; i < NUMBER_OF_RUNS; i++ ) {
res = getValueFromHashMap( i );
}
System.out.println( "HashMap<Character,Byte>:" );
System.out.println( System.currentTimeMillis()-time );
time = System.currentTimeMillis();
for( int i = 0; i < NUMBER_OF_RUNS; i++ ) {
char c = cs[i%cs.length];
res = getValueFromComputeMethod( c );
}
System.out.println( "JonSkeet's Direct Method:" );
System.out.println( System.currentTimeMillis()-time );
time = System.currentTimeMillis();
for( int i = 0; i < NUMBER_OF_RUNS; i++ ) {
res = getValueFromSwitch( i );
}
System.out.println( "Switch:" );
System.out.println( System.currentTimeMillis()-time );
}
private static byte getValueFromSwitch( int i ) {
byte res;
char ch = cs[i%cs.length];
switch( ch ) {
case '0':
res = 0;
break;
case '1':
res = 1;
break;
case '2':
res = 2;
break;
case '3':
res = 3;
break;
case '4':
res = 4;
break;
case '5':
res = 5;
break;
case '6':
res = 6;
break;
case '7':
res = 7;
break;
case '8':
res = 8;
break;
case '9':
res = 9;
break;
case 'a':
case 'A':
res = 10;
break;
case 'b':
case 'B':
res = 11;
break;
case 'c':
case 'C':
res = 12;
break;
case 'd':
case 'D':
res = 13;
break;
case 'e':
case 'E':
res = 14;
break;
case 'f':
case 'F':
res = 15;
break;
default:
throw new RuntimeException("unknown hex character: " + ch );
}
return res;
}
private static byte getValueFromComputeMethod( char c ) {
byte result = 0;
if (c >= '0' && c <= '9')
{
result = (byte)(c - '0');
}
if (c >= 'a' && c <= 'f')
{
result = (byte)(c - 'a' + 10);
}
if (c >= 'A' && c <= 'F')
{
result = (byte)(c - 'A' + 10);
}
return result;
}
private static byte getValueFromHashMap( int i ) {
return map.get( Character.valueOf( cs[i%cs.length] ) ).byteValue();
}
private static byte getValueFromArray( int i ) {
char c = cs[i%cs.length];
if (c < '0' || c > 'f') {
throw new IllegalArgumentException();
}
byte result = (byte)charValues[c-'0'];
if (res < 0) {
throw new IllegalArgumentException();
}
return result;
}
private static byte getDigit( int i ) {
return (byte)Character.digit( cs[i%cs.length], 16 );
}
private static byte getNumericValue( int i ) {
return (byte)Character.getNumericValue( cs[i%cs.length] );
}
}
Asked by: Melanie983 | Posted: 21-01-2022
Answer 1
A preinitialised array would be faster than a HashMap. Something like this:
int CharValues['f'-'0'+1] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, ... -1, 10, 11, 12, ...};
if (c < '0' || c > 'f') {
throw new IllegalArgumentException();
}
int n = CharValues[c-'0'];
if (n < 0) {
throw new IllegalArgumentException();
}
// n contains the digit value
You should benchmark this method against other methods (such as Jon Skeet's direct method) to determine which will be the fastest for your application.
Answered by: Emily430 | Posted: 22-02-2022Answer 2
A hash table would be relatively slow. This is pretty quick:
if (c >= '0' && c <= '9')
{
return c - '0';
}
if (c >= 'a' && c <= 'f')
{
return c - 'a' + 10;
}
if (c >= 'A' && c <= 'F')
{
return c - 'A' + 10;
}
throw new IllegalArgumentException();
Another option would be to try a switch/case statement. An array might be okay if it's in cache, but a miss could be expensive.
Answered by: Brianna394 | Posted: 22-02-2022Answer 3
I don't recall seeing this method before, but Mikko Rantanen pointed this equation out in a comment on the question, Code golf - hex to (raw) binary conversion
(char | 32) % 39 - 9
I don't know what it would benchmark as (perhaps someone can add it to the benchmark above and run it, but I'm guessing the % kills the performance) - but it's a neat, simple one-liner for single character hexadecimal to decimal conversion. Handles 0-9, A-F, a-f.
Answered by: Grace918 | Posted: 22-02-2022Answer 4
int CharValues[256] =
{
16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,
16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,0,1,2,3,4,5,6,7,8,9,16,16,16,16,16,16,16,
16,10,11,12,13,14,15,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,
16,10,11,12,13,14,15,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,
16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,
16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,
16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,
16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16
}
int n = CharValues[c];
if (n == 16)
throw new IllegalArgumentException();
// n contains the digit value
Answered by: Melissa184 | Posted: 22-02-2022
Answer 5
Using an array should be fastest.
An array could be of size 16, 16^2, 16^3, 16^4 etc..
Converting the number in larger groups than one would give a performance increase.
There will be a sweet spot where it is most worthwhile, possibly 4 digits (64k table).
Answered by: Roland605 | Posted: 22-02-2022Answer 6
Dude, I'm microcontroller programmer and in this tiny world, speed really matters. The fastest way to convert an 'ASCII' digit to byte (from 'A' to 0x0A) is using this small piece of code
c|=0x20;
return c<='9'? c+0xD0 : c+0xA9;
The magic of this command is simple, if you look the ascii table you'll find numbers starting with 0x3_, and letters at columns 0x4_ and 0x6_ respectively. 1) if you "or" the incoming char (variable "c") with 0x20, you'll be never changing numbers... but for letters you'll be converting uppercase to lowercase. Since we are looking for only A..F(a..f) values... I don't care others. 2) now the magic. To avoid subtractions, I use additive operators. the 0xD0 is the same of -0x30, and 0xA9 is the same of -'a'+10;
the branch instruction generated by the comparison is pretty simple and didn't take the overhead of table lookups and neither "mod" or other operators!
Enjoy...
Answered by: John952 | Posted: 22-02-2022Answer 7
Character.getNumericValue(char) is another way:
char c = 'a';
System.out.println(c + "->" + Character.getNumericValue(c));
Prints 'a->10' like you want for instance. Someone else would have to comment on the efficiency of a static metod call vs a HashMap lookup, or you could check it out for yourself. It seems cleaner/more readable to me though.
Answered by: Elise126 | Posted: 22-02-2022Answer 8
simple, but slow:
int i = Integer.parseInt(String.ValueOf(c), 16);
faster:
int i = Character.digit(c, 16);
I wont use any special code for "performance issues". If you really often use this, the JIT will create compiled code and execution will become fast. Keep your code clean. You may have a try and write a performace test comparing the execution time from the code above and any special implementation - i bet you wont get significant improvements.
Answered by: Julian767 | Posted: 22-02-2022Answer 9
I don't think you can beat a direct array lookup.
static final int[] precalc = new int['f'+1];
static {
for (char c='0'; c<='9'; c++) precalc[c] = c-'0';
for (char c='A'; c<='F'; c++) precalc[c] = c-'A';
for (char c='a'; c<='f'; c++) precalc[c] = c-'a';
}
System.out.println(precalc['f']);
Answered by: Patrick797 | Posted: 22-02-2022
Answer 10
Here's my tweaked version of Greg's code. On my box it's marginally faster - but probably within the noise. It avoids the lower bound check, and doesn't need to do any subtraction. Creating a 64K array and avoiding either bound check appeared to slow things down - but again, with timing like this it's virtually impossible to be sure what's real and what's noise.
public class HexParser
{
private static final byte VALUES = new int['f'];
// Easier to get right for bozos like me (Jon) than
// a hard-coded array :)
static
{
for (int i=0; i < VALUES.length; i++)
{
VALUES[i] = (byte) -1;
}
for (int i='0'; i <= '9'; i++)
{
VALUES[i] = (byte) i-'0';
}
for (int i='A'; i <= 'F'; i++)
{
VALUES[i] = (byte) (i-'A'+10);
}
for (int i='a'; i <= 'f'; i++)
{
VALUES[i] = (byte) (i-'a'+10);
}
}
public static byte parseHexChar(char c)
{
if (c > 'f')
{
throw new IllegalArgumentException();
}
byte ret = VALUES[c];
if (ret == -1)
{
throw new IllegalArgumentException();
}
return ret;
}
}
Answered by: Caroline373 | Posted: 22-02-2022
Answer 11
It worth noting that you are realing timing the % operation in most of your tests. This operation takes about the same amount of time as some of the other options.
private static byte lookUpTest(int i) {
return (byte) cs[i%cs.length];
}
Answered by: Blake155 | Posted: 22-02-2022
Answer 12
A table of 16-bit values where you could look up two digits at a time should be quicker than the 8-bit-value arrays suggested in other answers. You would be iterating the data twice as quickly, accessing the array half as often, and accessing shorts instead of bytes, all of which gives the CPU less to do and more to do it with.
The 8-bit values would be precomputed as x[Integer.toHexString(i).charAt[0]] = i
for 0 <= i < 256
. Then you would just lookup x[c]
for each char c
in the hex string. You would probably want to duplicate the entries for both upper-case and lower-case variants of each hex value.
A table of 32-bit values should be even quicker, depending on how much space you can afford.
Answered by: Miller214 | Posted: 22-02-2022Answer 13
According to my timings the following outperforms Jon Skeet's already pretty efficient method.
The idea is to take advantage of the test on range 'A'..'F' to also discard one of the ranges '0'..'9' or 'a'..'f'.
public static int hex2Dig2(char c) {
if (c < 'A') {
if (c >= '0' && c <= '9')
return c - '0';
} else if (c > 'F') {
if (c >= 'a' && c <= 'f')
return c - 'a' + 10;
} else {
return c - 'A' + 10;
}
return -1; // or throw exception
}
Answered by: Julian760 | Posted: 22-02-2022
Similar questions
performance - Java count up/down in hexadecimal
I know you can just use an integer and count up and down then convert to a hexadecimal string, but I need to use integers larger than the max value (up to 0xffffffffffffffffffff or 1208925819614629174706175).
What would be the most efficient way to count up to these types of numbers?
performance - Java very large heap sizes
Closed. This question needs to be more focused. It ...
java - Is there a performance difference between Javac debug on and off?
If I switch on the generating of debug info with Javac then the class files are 20-25% larger. Has this any performance effects on running the Java program? If yes on which conditions and how many. I expect a little impact on loading the classes because the files are larger but this should be minimal.
performance - Size of a byte in memory - Java
I have heard mixed opinions over the amount of memory that a byte takes up in a java program.
I am aware you can store no more than +127 in a java byte, and the documentation says that a byte is only 8 bits but here I am told that it actua...
java - How to improve Netbeans performance?
Is there a real way to get Netbeans to load and work faster?
It is too slow and gets worse when you have been coding for some time. It eats all my RAM.
I am on a Windows machine, specifically Windows Server 2008 Datacenter Edition x64,
4Gb of RAM, 3Ghz Core 2 Duo processor, etc. I am using the x64 JDK. I use the NOD32 Antivirus since for me it is the best in machine performance.
In Task Manage...
java - Is there a performance difference between a for loop and a for-each loop?
What, if any, is the performance difference between the following two loops?
for (Object o: objectArrayList) {
o.DoSomething();
}
and
for (int i=0; i<objectArrayList.size(); i++) {
objectArrayList.get(i).DoSomething();
}
performance - Java File Cursor
Is there some library for using some sort of cursor over a file? I have to read big files, but can't afford to read them all at once into memory. I'm aware of java.nio, but I want to use a higher level API.
A little backgrond: I have a tool written in GWT that analyzes submitted xml documents and then pretty prints the xml, among other things. Currently I'm writing the pretty printed xml to a temp file (my lib woul...
performance - Accelerate 2D images in Java *without* disturbing JMenus
Already implemented performance boosters :
- Get compatible image of GraphicsConfiguration to draw on
- Enable OpenGL pipeline in 1.5: Not possible due to severe artifacts
So far I am fine, the main profiled bottleneck of the program is drawing an image with several thousand tiles. Unfortunately it is not regular, else I simply could set pixels
and scale them.
I accerelated the image with VolatileImages and own ren...
performance - Measuring Java execution time, memory usage and CPU load for a code segment
For a particular segment of Java code, I'd like to measure:
Execution time (most likely thread execution time)
Memory usage
CPU load (specifically attributable to the code segment)
I'm a relative Java novice and am not familiar with how this might be achieved. I've been referred to
Can anyone quantify performance differences between C++ and Java?
Java was initially slow before the JIT but today performance is pretty close to C++. I want to know if someone has done measurable performance comparisons between the two languages? Where does Java fall short when compared to C++? Java provides many productivity gains to developers so they can write applications much quicker because of garbage college, lack of pointers, etc. Applications such as Firefox, Webkit ...
performance - ArrayList vs. Vectors in Java if thread safety isn't a concern
Is there really that much of a difference between the performance of Vector and ArrayList? Is it good practice to use ArrayLists at all times when thread safety isn't an issue?
Still can't find your answer? Check out these amazing Java communities for help...
Java Reddit Community | Java Help Reddit Community | Dev.to Java Community | Java Discord | Java Programmers (Facebook) | Java developers (Facebook)