Вопрос

Мы все знаем, какое влияние может оказать множество генерируемых исключений на производительность наших приложений, поэтому нам следует держаться подальше от таких вещей, как использование исключений для потока управления.После этого заявления я должен признаться, что при написании кода меня это не особо волновало.Я работал в основном на платформе Java, но в последнее время я делал это на платформе .NET и только что обнаружил этот удобный метод: public static bool TryParse(string s,out int result) , который позволяет вам преобразовать строку в int, не вызывая исключения.С этого момента я продолжаю им пользоваться.Я просто хотел спросить вас о ваших предпочтениях относительно использования public static bool TryParse(string s,out int result) или public static int ToInt32(string value).

И с точки зрения Java, просто указывая, что в ней отсутствует такой похожий метод, несмотря на то, что мы могли бы получить его с помощью таких вещей, как:

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

Спасибо.

Это было полезно?

Решение

Да, в Java отсутствует аналогичный метод, хотя без out параметры, которые на самом деле довольно сложно выразить (при желании вернуть примитив).Однако, как правило, в C # вы должны использовать TryParse если вы ожидаете, что значение иногда не будет целым числом, и ToInt32 в противном случае;таким образом, "исключительная" ситуация рассматривается как таковая.

В частности, если производительность является вашей главной причиной для желания TryParse, метод соответствия регулярным выражениям, который вы публикуете, значительно хуже."Затраты" на производительность исключений (которые на самом деле очень минимальны) ничтожны по сравнению с тем, насколько их неправильное использование может затруднить понимание потока управления.

Другие советы

Я не знаю насчет C #, но в Java исключения стоят дорого только тогда, когда они действительно генерируются, но тогда они действительно очень дороги.Если вы ожидаете, что значительная часть строк будет недопустимой, вам стоит сначала проверить их, даже если вы используете регулярное выражение.

Но не используйте String.matches() или Pattern.matches() чтобы применить регулярное выражение;эти методы перекомпилируют регулярное выражение каждый раз, когда вы их вызываете.Вместо этого заранее скомпилируйте регулярное выражение и сохраните его как объект Pattern, а затем выполните проверку с его помощью.В моих тестах при разборе списка из 10 000 строк, из которых 20% были недействительными, предварительная проверка с помощью шаблона выполняется почти в два раза быстрее, чем при использовании Integer.parseInt() в одиночку и улавливая исключения.

Однако это обсуждение применимо только в том случае, если вы выполняете много преобразований в замкнутом цикле.Если вы выполняете их только время от времени, например, когда принимаете пользовательский ввод, позволяя Integer.parseInt() выполните проверку - это нормально.И если вы решите выполнить проверку с помощью регулярного выражения, вам понадобится гораздо лучшее регулярное выражение, чем ^\d*$ - это регулярное выражение будет соответствовать пустой строке, а также "числам", большим, чем Integer.MAX_VALUE, и это вообще не будет соответствовать отрицательным числам.

Для этой цели в Java вы можете использовать хорошо известные StringUtils (на commons-lang), у этого класса есть метод IsNumeric ( числовой )

Вероятно, вы можете взглянуть на код, для которого написали эти ребята эта функция:

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;
 }

Я не говорю, что это самый эффективный способ сделать это, но для вас есть другая альтернатива без использования регулярных выражений.Удачи вам!

И с точки зрения Java, просто указываю, что в нем отсутствует такой похожий метод, несмотря на то, что мы могли бы получить его с помощью таких вещей, как:

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

Предсказать, если Integer.parseInt(myString) если бы возникло исключение, нужно было бы проделать еще больше работы.Строка может начинаться с -.Также значение int не может содержать более 10 значащих цифр.Таким образом, более надежным выражением было бы ^-?0*\d{1,10}$.Но даже это выражение не предсказало бы каждое Исключение, потому что оно все еще слишком неточно.

Сгенерировать надежное регулярное выражение возможно.Но это было бы очень долго.Также возможно реализовать метод, который точно определяет, будет ли parseInt выдавать исключение.IT мог бы выглядеть вот так:

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 # для проверить если строка может быть преобразована, вы бы использовали 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;
}

Я просто хотел спросить вас о ваших предпочтениях относительно использования public static bool TryParse(string s,out int result) или public static int ToInt32(строковое значение).

Да, я использую TryParse, за исключением тех случаев, когда я ожидаю, что значение будет всегда быть действительным.Я нахожу, что это читается чище, чем использование исключений.Даже если мне нужно исключение, я обычно хочу настроить сообщение или создать свое собственное пользовательское исключение;следовательно, я использую TryParse и вручную создаю исключение.

Как в Java, так и в C #, я пытаюсь уловить минимальный возможен набор исключений.В Java это означает, что я должен отдельно перехватывать исключения NullPointerException и NumberFormatException в ответ на Number.valueOf(...);в качестве альтернативы, я могу перехватить "Исключение" и рискнуть подхватить что-то непреднамеренное.С TryParse в C # я вообще не беспокоюсь об этом.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top