I just announced the new Spring Boot 2 material, coming in REST With Spring:

>> CHECK OUT THE COURSE

1. Introduction

Oftentimes while operating upon Strings, we need to figure out whether a String is a valid number or not.

In this tutorial, we’ll explore multiple ways to detect if the given String is numeric, first using plain Java, then regular expressions and finally by using external libraries.

Once we’re done discussing various implementations, we’ll use benchmarks to get an idea of which methods are optimal.

Let’s start with some prerequisites before we head on to the main content.

2. Prerequisite

In the latter part of this article, we’ll be using Apache Commons external library. To include this dependency, add the following lines in pom.xml:

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.7</version>
</dependency>

The latest version of this library can be found on Maven Central.

3. Plain Java

Perhaps the easiest and the most reliable way to check whether a String is numeric or not is by parsing it using Java’s built-in methods:

  1. Integer.parseInt(String)
  2. Float.parseFloat(String)
  3. Double.parseDouble(String)
  4. Long.parseLong(String)
  5. new BigInteger(String)

If these methods don’t throw any NumberFormatException, then it means that the parsing was successful and the String is numeric:

public static boolean isNumeric(String strNum) {
    try {
        double d = Double.parseDouble(strNum);
    } catch (NumberFormatException | NullPointerException nfe) {
        return false;
    }
    return true;
}

Let’s see this method in action:

assertThat(isNumeric("22")).isTrue();
assertThat(isNumeric("5.05")).isTrue();
assertThat(isNumeric("-200")).isTrue(); 
assertThat(isNumeric("10.0d")).isTrue();
assertThat(isNumeric("   22   ")).isTrue();
 
assertThat(isNumeric(null)).isFalse();
assertThat(isNumeric("")).isFalse();
assertThat(isNumeric("abc")).isFalse();

In our isNumeric() method, we’re just checking for values that are of type Double, but this method can also be modified to check for Integer, Float, Long and large numbers by using any of the parse methods that we have enlisted earlier.

These methods are also discussed in the Java String Conversions article.

4. Regular Expressions

Now let’s use regex -?\d+(\.\d+)?to match numeric Strings consisting of the positive or negative integer and floats.

But this goes without saying, that we can definitely modify this regex to identify and cope up with a wide range of rules. Here, we’ll keep it simple.

Let’s break down this regex and see how it works:

  • -? – this part identifies if the given number is negative, the dash “” searches for dash literally and the question mark “?” marks its presence as an optional one
  • \d+ – this searches for one or more digits
  • (\.\d+)? – this part of regex is to identify float numbers. Here we’re searching for one or more digits followed by a period. The question mark, in the end, signifies that this complete group is optional

The regular expressions is a very broad topic, and to get a brief overview, visit this linked Baeldung article.

For now, let’s create a method using the above regular expression:

public static boolean isNumeric(String strNum) {
    return strNum.matches("-?\\d+(\\.\\d+)?");
}

Let’s now look at some assertions for the above method:

assertThat(isNumeric("22")).isTrue();
assertThat(isNumeric("5.05")).isTrue();
assertThat(isNumeric("-200")).isTrue();
 
assertThat(isNumeric("abc")).isFalse();

 5. Apache Commons

In this section, we’ll discuss various methods available in Apache Commons library.

5.1. NumberUtils.isCreatable(String) 

NumberUtils from Apache Commons provides with a static method NumberUtils.isCreatable(String) which checks whether a   valid Java number or not.

This method accepts:

  1. Hexadecimal numbers starting with 0x or 0X
  2. Octal numbers starting with a leading 0
  3. Scientific notation (for example 1.05e-10)
  4. Numbers marked with a type qualifier (for example 1L or 2.2d)

If the supplied string is null or empty/blank, then it’s not considered as a number and this method will return false in that case.

Let’s run some tests using this method:

assertThat(NumberUtils.isCreatable("22")).isTrue();
assertThat(NumberUtils.isCreatable("5.05")).isTrue();
assertThat(NumberUtils.isCreatable("-200")).isTrue();
assertThat(NumberUtils.isCreatable("10.0d")).isTrue();
assertThat(NumberUtils.isCreatable("1000L")).isTrue();
assertThat(NumberUtils.isCreatable("0xFF")).isTrue();
assertThat(NumberUtils.isCreatable("07")).isTrue();
assertThat(NumberUtils.isCreatable("2.99e+8")).isTrue();
 
assertThat(NumberUtils.isCreatable(null)).isFalse();
assertThat(NumberUtils.isCreatable("")).isFalse();
assertThat(NumberUtils.isCreatable("abc")).isFalse();
assertThat(NumberUtils.isCreatable(" 22 ")).isFalse();
assertThat(NumberUtils.isCreatable("09")).isFalse();

Note how we’re getting true assertions for hexadecimal numbers, octal numbers and scientific notations in lines 6, 7 and 8 respectively.

Also in line 14, the string “09” returns false because the preceding “0” indicates that this is an octal number and “09” is not a valid octal number.

For every input that returns true with this method, we can use NumberUtils.createNumber(String) which will give us the valid number.

5.2. NumberUtils.isParsable(String) 

The NumberUtils.isParsable(String) method checks whether the given String is parsable or not.

Parsable numbers are those that are parsed successfully by any of parse method like Integer.parseInt(String), Long.parseLong(String), Float.parseFloat(String) or Double.parseDouble(String).

Unlike NumberUtils.isCreatable(), this method won’t accept hexadecimal numbers, scientific notations or strings ending with any type qualifier, i.e. ‘f’, ‘F’, ‘d’ ,’D’ ,’l’or‘L’.

Let’s look at some affirmations:

assertThat(NumberUtils.isParsable("22")).isTrue();
assertThat(NumberUtils.isParsable("-23")).isTrue();
assertThat(NumberUtils.isParsable("2.2")).isTrue();
assertThat(NumberUtils.isParsable("09")).isTrue();

assertThat(NumberUtils.isParsable(null)).isFalse();
assertThat(NumberUtils.isParsable("")).isFalse();
assertThat(NumberUtils.isParsable("6.2f")).isFalse();
assertThat(NumberUtils.isParsable("9.8d")).isFalse();
assertThat(NumberUtils.isParsable("22L")).isFalse();
assertThat(NumberUtils.isParsable("0xFF")).isFalse();
assertThat(NumberUtils.isParsable("2.99e+8")).isFalse();

In line 4, unlike NumberUtils.isCreatable(), the number starting with string “0” isn’t considered as an octal number, but a normal decimal number and hence it returns true.

We can use this method as a replacement for what we did in section 3, where we’re trying to parse a number and checking for an error.

5.3. StringUtils.isNumeric(CharSequence) 

The method StringUtils.isNumeric(CharSequence) checks strictly for Unicode digits. This means:

  1. Any digits from any language that is a Unicode digit is acceptable
  2. Since a decimal point is not considered as a Unicode digit, it’s not valid
  3. Leading signs (either positive or negative) are also not acceptable

Let’s now see this method in action:

assertThat(StringUtils.isNumeric("123")).isTrue();
assertThat(StringUtils.isNumeric("١٢٣")).isTrue();
assertThat(StringUtils.isNumeric("१२३")).isTrue();
 
assertThat(StringUtils.isNumeric(null)).isFalse();
assertThat(StringUtils.isNumeric("")).isFalse();
assertThat(StringUtils.isNumeric("  ")).isFalse();
assertThat(StringUtils.isNumeric("12 3")).isFalse();
assertThat(StringUtils.isNumeric("ab2c")).isFalse();
assertThat(StringUtils.isNumeric("12.3")).isFalse();
assertThat(StringUtils.isNumeric("-123")).isFalse();

Note that the input parameters in lines 2 and 3 are representing numbers 123 in Arabic and Devanagari respectively. Since they’re valid Unicode digits, this method returns true on them.

5.4. StringUtils.isNumericSpace(CharSequence)

The StringUtils.isNumericSpace(CharSequence) checks strictly for Unicode digits and/or space. This is same as StringUtils.isNumeric() with the only difference being that it accepts spaces as well, not only leading and trailing spaces but also if they’re in between numbers:

assertThat(StringUtils.isNumericSpace("123")).isTrue();
assertThat(StringUtils.isNumericSpace("١٢٣")).isTrue();
assertThat(StringUtils.isNumericSpace("")).isTrue();
assertThat(StringUtils.isNumericSpace("  ")).isTrue();
assertThat(StringUtils.isNumericSpace("12 3")).isTrue();
 
assertThat(StringUtils.isNumericSpace(null)).isFalse();
assertThat(StringUtils.isNumericSpace("ab2c")).isFalse();
assertThat(StringUtils.isNumericSpace("12.3")).isFalse();
assertThat(StringUtils.isNumericSpace("-123")).isFalse();

6. Benchmarks

Before we conclude this article, let’s quickly go through benchmark results that will help us to analyze as which of the above-mentioned methods are optimal approaches:

Benchmark                                     Mode   Cnt    Score     Error  Units
Benchmarking.usingCoreJava                    avgt   20   152.061 ±  24.300  ns/op
Benchmarking.usingRegularExpressions          avgt   20  1299.258 ± 175.688  ns/op
Benchmarking.usingNumberUtils_isCreatable     avgt   20    63.811 ±   5.638  ns/op
Benchmarking.usingNumberUtils_isParsable      avgt   20    58.706 ±   5.299  ns/op
Benchmarking.usingStringUtils_isNumeric       avgt   20    35.599 ±   8.498  ns/op
Benchmarking.usingStringUtils_isNumericSpace  avgt   20    37.010 ±   4.394  ns/op

As we can see, the most costly operation is with regular expressions, followed by core Java-based solution. All other operations using Apache Commons library are by-and-large same.

7. Conclusion

In this article, we explored different ways to find if a String is numeric or not. We looked at both solutions – built-in methods and also external libraries.

As always, the implementation of all examples and code snippets given above including the code used to perform benchmarks can be found over on GitHub.

I just announced the new Spring Boot 2 material, coming in REST With Spring:

>> CHECK OUT THE LESSONS

newest oldest most voted
JoeHx
Guest

I was wondering how using try-catches would compare to using regular expressions. It’s interesting the rather large difference between those two and the rest.

John Douglass
Guest
John Douglass

You will probably get a good performance boost for regex by reusing a Pattern object, e.g.:

private static Pattern pNumeric = Pattern.compile(“-?\\d+(\\.\\d+)?”);
public static boolean isNumeric(String strNum) {
return pNumeric.matcher(strNum).matches();
}

It was about 3-4x faster for me, but still much slower than the alternatives.

Loredana Crusoveanu
Editor

Interesting, thanks. We’ll update the article.