Java string immutability and NumberFormatException For input string: ""

78 Views Asked by At
static Number parse(String s) {
    if (s == null || s.trim().length() == 0) {
      return 0;
    }
    return Long.parseLong(s);
}

This piece of code intermittently throws java.lang.NumberFormatException: For input string: "".

Running on Java version 11.0.20+8-LTS-sapmachine.

How is empty string passed into parseLong? I understand s.trim() and s are different instances but doesn't s.trim().length() != 0 infer s is non-empty?

UPDATE: Apologies, I had left out the fact that I am parsing with a DecimalFormat instance (naively thought it did not matter).

static java.text.DecimalFormat df = new java.text.DecimalFormat("#");

static Number parse(String s) throws java.text.ParseException {
    if (s == null || s.trim().length() == 0) {
        return 0;
    }
    return df.parse(s);
}

This code throws java.lang.NumberFormatException: For input string: "" from time to time on a live server. Stack trace:

java.lang.NumberFormatException: For input string: ""
    at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
    at java.lang.Long.parseLong(Long.java:702)
    at java.lang.Long.parseLong(Long.java:817)
    at java.text.DigitList.getLong(DigitList.java:195)
    at java.text.DecimalFormat.parse(DecimalFormat.java:2121)
    at java.text.NumberFormat.parse(NumberFormat.java:429)
2

There are 2 best solutions below

0
Girish Chandra Mishra On
public static Number parse(String s) throws NumberFormatException {
  if (s == null || s.isBlank()) {
    return 0;
  }
  return Long.parseLong(s);
}
0
Jon On

Looking into DigitList class, s definitely is not the input string here. The DigitList's use of StringBuffer tempBuffer is not thread-safe.

private StringBuffer getStringBuffer() {
    if (tempBuffer == null) {
        tempBuffer = new StringBuffer(MAX_COUNT);
    } else {
        tempBuffer.setLength(0);
    }
    return tempBuffer;
}

tempBuffer.setLength(0) is what is causing the empty input string. DecimalFormat.digitList and DigitList.tempBuffer are both class members and I am using a singleton DecimalFormat instance creating a race condition.

I am able to reproduce the problem with this line of code

IntStream
  .range(1, Runtime.getRuntime().availableProcessors() * 3)
  .parallel()
  .forEach(s -> parse("123"));

Without the parallel() mode, the issue does not occur.

Having found the root of the issue, I also found this question is essentially a duplicate of https://stackoverflow.com/a/4021932/22569932 where good explanation and solution are provided.