【问题标题】:Convert.ToInt32 versus TryParseConvert.ToInt32 与 TryParse
【发布时间】:2009-05-27 10:37:17
【问题描述】:

我们都知道大量抛出的异常会对应用程序的性能产生影响,因此,我们应该远离使用异常进行控制流之类的事情。 在此声明之后,我必须承认,在编码时我并不太关心这一点。我主要在 Java 平台上工作,但最近我在 .NET 平台上做,发现了这个方便的方法:public static bool TryParse(string s,out int result) ,它允许您将 String 转换为 int 而不会引发异常。从那一刻起,我一直在使用它。我只是想问你关于使用public static bool TryParse(string s,out int result)public static int ToInt32(string value) 的偏好。

从 Java 的角度来看,只是指出它缺少这样一个类似的方法,尽管我们可以通过以下方式获得它:

boolean isInteger = Pattern.matches("^\d*$", myString);

谢谢。

【问题讨论】:

    标签: c# java performance exception tryparse


    【解决方案1】:

    是的,Java 缺少一个类似的方法,尽管没有out 参数,它实际上很难表达(同时想要返回一个原语)。不过,一般来说,在 C# 中,如果您希望值有时不是整数,则应使用 TryParse,否则应使用 ToInt32;这样,“异常”情况就被这样对待了。

    特别是如果性能是您想要TryParse 的主要原因,那么您发布的正则表达式匹配方法要差得多。异常的性能“开销”(实际上,非常小)与错误使用它们的程度相比相形见绌。

    【讨论】:

    • 顺便说一句,在 Java 中会有其他方法来获取它。一种是使用 Integer 而不是 int,另一种是使用 int 放入数组并将其传递给方法。
    • 是的,这就是为什么我提到“同时想要返回原始”部分。从本质上讲,这是可能的,但绝不是整洁的。
    【解决方案2】:

    我不了解 C#,但在 Java 中,异常只有在实际抛出时才会昂贵,但它们确实非常昂贵。如果您预计大部分字符串无效,那么首先验证它们是值得的,即使您使用正则表达式也是如此。

    但不要使用String.matches()Pattern.matches() 来应用正则表达式;每次调用这些方法时,它们都会重新编译正则表达式。相反,提前编译正则表达式并将其保存为 Pattern 对象,然后使用它进行验证。在我的测试中,解析 10,000 个字符串列表,其中 20% 无效,使用 Pattern 进行预验证几乎是单独使用 Integer.parseInt() 并捕获异常的两倍。

    但是,此讨论仅适用于您在紧密循环中进行大量转换的情况。如果您只是偶尔做一次,比如当您接受用户输入时,让Integer.parseInt() 进行验证就可以了。如果您确实选择使用正则表达式进行验证,您将需要一个比^\d*$ 更好的正则表达式——该正则表达式将匹配空字符串以及大于Integer.MAX_VALUE 的“数字”,并且它不会匹配负数数字。

    【讨论】:

      【解决方案3】:

      为此,您可以在 Java 中使用众所周知的 StringUtils(在 commons-lang 上),这个类有一个方法 isNumeric

      也许你可以看看那些人为that function写的代码:

      public static boolean isNumeric(String str) {
        if (str == null) {
          return false;
        }
        int sz = str.length();
        for (int i = 0; i < sz; i++) {
          if (Character.isDigit(str.charAt(i)) == false) {
            return false;
          }
        }
        return true;
       }
      

      我并不是说这是最有效的方法,但您还有另一种选择,无需使用正则表达式。 祝你好运!

      【讨论】:

      • 这个“== false”的东西是怎么回事?我不敢相信我在 Apache 源代码中看到了这一点!
      • “== false”的问题是如果你不小心只使用了一个“=”会发生什么。表达式的类型仍然是布尔值,所以它可以编译,但你最终会得到一个可能是微妙的、间歇性的错误。摆脱它只是一个好习惯。
      • if ( !Character.isDigit(str.charAt(i)) )
      • @AlanMoore,不适用于foo() == false。您不能为方法的返回值赋值。在其他情况下,例如foo == false,当然可能会出现问题。所以最好写false == foo,比true == (false == foo)好一点。
      【解决方案4】:

      从 Java 的角度来看, 只是指出它缺少这样一个 类似的方法,尽管我们可以得到 它通过以下方式:

      boolean isInteger = Pattern.matches("^\d*$", myString);
      

      要预测Integer.parseInt(myString) 是否会抛出异常,还有更多工作要做。字符串可以以- 开头。此外,一个 int 的有效数字不能超过 10 个。所以更可靠的表达式是^-?0*\d{1,10}$。但是即使这个表达式也不能预测每个异常,因为它仍然太不精确。

      生成可靠的正则表达式是可能的。但这会很长。也可以实现一个精确确定 parseInt 是否会抛出异常的方法。它可能如下所示:

      static boolean wouldParseIntThrowException(String s) {
          if (s == null || s.length() == 0) {
              return true;
          }
      
          char[] max = Integer.toString(Integer.MAX_VALUE).toCharArray();
          int i = 0, j = 0, len = s.length();
          boolean maybeOutOfBounds = true;
      
          if (s.charAt(0) == '-') {
              if (len == 1) {
                  return true; // s == "-"
              }
              i = 1;
              max[max.length - 1]++; // 2147483647 -> 2147483648
          }
          while (i < len && s.charAt(i) == '0') {
              i++;
          }
          if (max.length < len - i) {
              return true; // too long / out of bounds
          } else if (len - i < max.length) {
              maybeOutOfBounds = false;
          }
          while (i < len) {
              char digit = s.charAt(i++);
              if (digit < '0' || '9' < digit) {
                  return true;
              } else if (maybeOutOfBounds) {
                  char maxdigit = max[j++];
                  if (maxdigit < digit) {
                      return true; // out of bounds
                  } else if (digit < maxdigit) {
                      maybeOutOfBounds = false;
                  }
              }
          }
          return false;
      }
      

      我不知道哪个版本更有效。这主要取决于上下文哪种检查是合理的。

      在 C# 中 check 如果可以转换字符串,您将使用 TryParse。如果它返回 true,那么它作为副产品 得到 同时转换。这是一个简洁的功能,我认为仅重新实现 parseInt 以返回 null 而不是抛出异常没有问题。

      但是,如果您不想重新实现解析方法,那么手头有一组可以根据情况使用的方法仍然会很好。它们可能如下所示:

      private static Pattern QUITE_ACCURATE_INT_PATTERN = Pattern.compile("^-?0*\\d{1,10}$");
      
      static Integer tryParseIntegerWhichProbablyResultsInOverflow(String s) {
          Integer result = null;
          if (!wouldParseIntThrowException(s)) {
              try {
                  result = Integer.parseInt(s);
              } catch (NumberFormatException ignored) {
                  // never happens
              }
          }
          return result;
      }
      
      static Integer tryParseIntegerWhichIsMostLikelyNotEvenNumeric(String s) {
          Integer result = null;
          if (s != null && s.length() > 0 && QUITE_ACCURATE_INT_PATTERN.matcher(s).find()) {
              try {
                  result = Integer.parseInt(s);
              } catch (NumberFormatException ignored) {
              // only happens if the number is too big
              }
          }
          return result;
      }
      
      static Integer tryParseInteger(String s) {
          Integer result = null;
          if (s != null && s.length() > 0) {
              try {
                  result = Integer.parseInt(s);
              } catch (NumberFormatException ignored) {
              }
          }
          return result;
      }
      
      static Integer tryParseIntegerWithoutAnyChecks(String s) {
          try {
              return Integer.parseInt(s);
          } catch (NumberFormatException ignored) {
          }
          return null;
      }
      

      【讨论】:

        【解决方案5】:

        我只是想问你关于使用 public static bool TryParse(string s,out int result) 或 public static int ToInt32(string value) 的偏好。

        是的,我使用 TryParse,除非我希望值 始终 有效。我发现它比使用异常更清晰。即使我想要异常,我通常也想自定义消息或抛出我自己的自定义异常;因此,我使用 TryParse 并手动抛出异常。

        在 Java 和 C# 中,我尝试捕获可能的最小 组异常。在 Java 中,这意味着我必须分别捕获 NullPointerException 和 NumberFormatException 以响应 Number.ValueOf(...);或者,我可以捕捉到“异常”并冒险捕捉到意外的东西。使用 C# 中的 TryParse,我完全不用担心。

        【讨论】:

          猜你喜欢
          • 2013-01-28
          • 2019-03-15
          • 2011-01-26
          • 1970-01-01
          • 2010-10-02
          • 1970-01-01
          • 1970-01-01
          • 2012-11-16
          相关资源
          最近更新 更多