What's the best way to check if a String represents an integer in Java?

后端 未结 30 1435
野趣味
野趣味 2020-11-22 05:45

I normally use the following idiom to check if a String can be converted to an integer.

public boolean isInteger( String input ) {
    try {
        Integer.         


        
相关标签:
30条回答
  • 2020-11-22 06:28

    I do not like method with regex, because regex can not check ranges (Integer.MIN_VALUE, Integer.MAX_VALUE).

    If you expect int value in most cases, and not int is something uncommon, then I suggest version with Integer.valueOf or Integer.parseInt with NumberFormatException catching. Advantage of this approach - your code has good readability:

    public static boolean isInt(String s) {
      try {
        Integer.parseInt(s);
        return true;
      } catch (NumberFormatException nfe) {
        return false;
      }
    }
    

    If you need to check if String is integer, and care about perfomance then the best way will be to use java jdk implementation of Integer.parseInt, but little modified (replacing throw with return false):

    This function has good perfomence and garantee right result:

       public static boolean isInt(String s) {
        int radix = 10;
    
        if (s == null) {
            return false;
        }
    
        if (radix < Character.MIN_RADIX) {
            return false;
        }
    
        if (radix > Character.MAX_RADIX) {
            return false;
        }
    
        int result = 0;
        boolean negative = false;
        int i = 0, len = s.length();
        int limit = -Integer.MAX_VALUE;
        int multmin;
        int digit;
    
        if (len > 0) {
            char firstChar = s.charAt(0);
            if (firstChar < '0') { // Possible leading "+" or "-"
                if (firstChar == '-') {
                    negative = true;
                    limit = Integer.MIN_VALUE;
                } else if (firstChar != '+')
                    return false;
    
                if (len == 1) // Cannot have lone "+" or "-"
                    return false;
                i++;
            }
            multmin = limit / radix;
            while (i < len) {
                // Accumulating negatively avoids surprises near MAX_VALUE
                digit = Character.digit(s.charAt(i++), radix);
                if (digit < 0) {
                    return false;
                }
                if (result < multmin) {
                    return false;
                }
                result *= radix;
                if (result < limit + digit) {
                    return false;
                }
                result -= digit;
            }
        } else {
            return false;
        }
        return true;
    }
    
    0 讨论(0)
  • 2020-11-22 06:29

    You have it, but you should only catch NumberFormatException.

    0 讨论(0)
  • 2020-11-22 06:29

    Did a quick benchmark. Exceptions aren't actually that expensivve, unless you start popping back multiple methods and the JVM has to do a lot of work to get the execution stack in place. When staying in the same method, they aren't bad performers.

     public void RunTests()
     {
         String str = "1234567890";
    
         long startTime = System.currentTimeMillis();
         for(int i = 0; i < 100000; i++)
             IsInt_ByException(str);
         long endTime = System.currentTimeMillis();
         System.out.print("ByException: ");
         System.out.println(endTime - startTime);
    
         startTime = System.currentTimeMillis();
         for(int i = 0; i < 100000; i++)
             IsInt_ByRegex(str);
         endTime = System.currentTimeMillis();
         System.out.print("ByRegex: ");
         System.out.println(endTime - startTime);
    
         startTime = System.currentTimeMillis();
         for(int i = 0; i < 100000; i++)
             IsInt_ByJonas(str);
         endTime = System.currentTimeMillis();
         System.out.print("ByJonas: ");
         System.out.println(endTime - startTime);
     }
    
     private boolean IsInt_ByException(String str)
     {
         try
         {
             Integer.parseInt(str);
             return true;
         }
         catch(NumberFormatException nfe)
         {
             return false;
         }
     }
    
     private boolean IsInt_ByRegex(String str)
     {
         return str.matches("^-?\\d+$");
     }
    
     public boolean IsInt_ByJonas(String str)
     {
         if (str == null) {
                 return false;
         }
         int length = str.length();
         if (length == 0) {
                 return false;
         }
         int i = 0;
         if (str.charAt(0) == '-') {
                 if (length == 1) {
                         return false;
                 }
                 i = 1;
         }
         for (; i < length; i++) {
                 char c = str.charAt(i);
                 if (c <= '/' || c >= ':') {
                         return false;
                 }
         }
         return true;
     }
    

    Output:

    ByException: 31

    ByRegex: 453 (note: re-compiling the pattern every time)

    ByJonas: 16

    I do agree that Jonas K's solution is the most robust too. Looks like he wins :)

    0 讨论(0)
  • 2020-11-22 06:30

    Since there's possibility that people still visit here and will be biased against Regex after the benchmarks... So i'm gonna give an updated version of the benchmark, with a compiled version of the Regex. Which opposed to the previous benchmarks, this one shows Regex solution actually has consistently good performance.

    Copied from Bill the Lizard and updated with compiled version:

    private final Pattern pattern = Pattern.compile("^-?\\d+$");
    
    public void runTests() {
        String big_int = "1234567890";
        String non_int = "1234XY7890";
    
        long startTime = System.currentTimeMillis();
        for(int i = 0; i < 100000; i++)
                IsInt_ByException(big_int);
        long endTime = System.currentTimeMillis();
        System.out.print("ByException - integer data: ");
        System.out.println(endTime - startTime);
    
        startTime = System.currentTimeMillis();
        for(int i = 0; i < 100000; i++)
                IsInt_ByException(non_int);
        endTime = System.currentTimeMillis();
        System.out.print("ByException - non-integer data: ");
        System.out.println(endTime - startTime);
    
        startTime = System.currentTimeMillis();
        for(int i = 0; i < 100000; i++)
                IsInt_ByRegex(big_int);
        endTime = System.currentTimeMillis();
        System.out.print("\nByRegex - integer data: ");
        System.out.println(endTime - startTime);
    
        startTime = System.currentTimeMillis();
        for(int i = 0; i < 100000; i++)
                IsInt_ByRegex(non_int);
        endTime = System.currentTimeMillis();
        System.out.print("ByRegex - non-integer data: ");
        System.out.println(endTime - startTime);
    
        startTime = System.currentTimeMillis();
        for (int i = 0; i < 100000; i++)
                IsInt_ByCompiledRegex(big_int);
        endTime = System.currentTimeMillis();
        System.out.print("\nByCompiledRegex - integer data: ");
        System.out.println(endTime - startTime);
    
        startTime = System.currentTimeMillis();
        for (int i = 0; i < 100000; i++)
                IsInt_ByCompiledRegex(non_int);
        endTime = System.currentTimeMillis();
        System.out.print("ByCompiledRegex - non-integer data: ");
        System.out.println(endTime - startTime);
    
    
        startTime = System.currentTimeMillis();
        for(int i = 0; i < 100000; i++)
                IsInt_ByJonas(big_int);
        endTime = System.currentTimeMillis();
        System.out.print("\nByJonas - integer data: ");
        System.out.println(endTime - startTime);
    
        startTime = System.currentTimeMillis();
        for(int i = 0; i < 100000; i++)
                IsInt_ByJonas(non_int);
        endTime = System.currentTimeMillis();
        System.out.print("ByJonas - non-integer data: ");
        System.out.println(endTime - startTime);
    }
    
    private boolean IsInt_ByException(String str)
    {
        try
        {
            Integer.parseInt(str);
            return true;
        }
        catch(NumberFormatException nfe)
        {
            return false;
        }
    }
    
    private boolean IsInt_ByRegex(String str)
    {
        return str.matches("^-?\\d+$");
    }
    
    private boolean IsInt_ByCompiledRegex(String str) {
        return pattern.matcher(str).find();
    }
    
    public boolean IsInt_ByJonas(String str)
    {
        if (str == null) {
                return false;
        }
        int length = str.length();
        if (length == 0) {
                return false;
        }
        int i = 0;
        if (str.charAt(0) == '-') {
                if (length == 1) {
                        return false;
                }
                i = 1;
        }
        for (; i < length; i++) {
                char c = str.charAt(i);
                if (c <= '/' || c >= ':') {
                        return false;
                }
        }
        return true;
    }
    

    Results:

    ByException - integer data: 45
    ByException - non-integer data: 465
    
    ByRegex - integer data: 272
    ByRegex - non-integer data: 131
    
    ByCompiledRegex - integer data: 45
    ByCompiledRegex - non-integer data: 26
    
    ByJonas - integer data: 8
    ByJonas - non-integer data: 2
    
    0 讨论(0)
  • 2020-11-22 06:30

    I copied the code from rally25rs answer and added some tests for non-integer data. The results are undeniably in favor of the method posted by Jonas Klemming. The results for the Exception method that I originally posted are pretty good when you have integer data, but they're the worst when you don't, while the results for the RegEx solution (that I'll bet a lot of people use) were consistently bad. See Felipe's answer for a compiled regex example, which is much faster.

    public void runTests()
    {
        String big_int = "1234567890";
        String non_int = "1234XY7890";
    
        long startTime = System.currentTimeMillis();
        for(int i = 0; i < 100000; i++)
            IsInt_ByException(big_int);
        long endTime = System.currentTimeMillis();
        System.out.print("ByException - integer data: ");
        System.out.println(endTime - startTime);
    
        startTime = System.currentTimeMillis();
        for(int i = 0; i < 100000; i++)
            IsInt_ByException(non_int);
        endTime = System.currentTimeMillis();
        System.out.print("ByException - non-integer data: ");
        System.out.println(endTime - startTime);
    
        startTime = System.currentTimeMillis();
        for(int i = 0; i < 100000; i++)
            IsInt_ByRegex(big_int);
        endTime = System.currentTimeMillis();
        System.out.print("\nByRegex - integer data: ");
        System.out.println(endTime - startTime);
    
        startTime = System.currentTimeMillis();
        for(int i = 0; i < 100000; i++)
            IsInt_ByRegex(non_int);
        endTime = System.currentTimeMillis();
        System.out.print("ByRegex - non-integer data: ");
        System.out.println(endTime - startTime);
    
        startTime = System.currentTimeMillis();
        for(int i = 0; i < 100000; i++)
            IsInt_ByJonas(big_int);
        endTime = System.currentTimeMillis();
        System.out.print("\nByJonas - integer data: ");
        System.out.println(endTime - startTime);
    
        startTime = System.currentTimeMillis();
        for(int i = 0; i < 100000; i++)
            IsInt_ByJonas(non_int);
        endTime = System.currentTimeMillis();
        System.out.print("ByJonas - non-integer data: ");
        System.out.println(endTime - startTime);
    }
    
    private boolean IsInt_ByException(String str)
    {
        try
        {
            Integer.parseInt(str);
            return true;
        }
        catch(NumberFormatException nfe)
        {
            return false;
        }
    }
    
    private boolean IsInt_ByRegex(String str)
    {
        return str.matches("^-?\\d+$");
    }
    
    public boolean IsInt_ByJonas(String str)
    {
        if (str == null) {
                return false;
        }
        int length = str.length();
        if (length == 0) {
                return false;
        }
        int i = 0;
        if (str.charAt(0) == '-') {
                if (length == 1) {
                        return false;
                }
                i = 1;
        }
        for (; i < length; i++) {
                char c = str.charAt(i);
                if (c <= '/' || c >= ':') {
                        return false;
                }
        }
        return true;
    }
    

    Results:

    ByException - integer data: 47
    ByException - non-integer data: 547
    
    ByRegex - integer data: 390
    ByRegex - non-integer data: 313
    
    ByJonas - integer data: 0
    ByJonas - non-integer data: 16
    
    0 讨论(0)
  • 2020-11-22 06:32

    You can use the matches method of the string class. The [0-9] represents all the values it can be, the + means it must be at least one character long, and the * means it can be zero or more characters long.

    boolean isNumeric = yourString.matches("[0-9]+"); // 1 or more characters long, numbers only
    boolean isNumeric = yourString.matches("[0-9]*"); // 0 or more characters long, numbers only
    
    0 讨论(0)
提交回复
热议问题