Regex или обработка исключений? - PullRequest
3 голосов
/ 08 марта 2011

Какой из следующих способов лучше проверить, является ли строка плавающей?

try{
 Double.parseDouble(strVal);
}catch(NumberFormatException e){
 //My Logic
}

или

if(!strVal.matches("[-+]?\\d*\\.?\\d+")){
 //My Logic
}

С точки зрения производительности, сопровождения и читаемости?

И да, я хотел бы знать, какая из них является хорошей практикой кодирования?

Ответы [ 7 ]

4 голосов
/ 08 марта 2011

Личное мнение - из кода, который я видел, я ожидаю, что большинство разработчиков будут стремиться к блокам try - catch. Функция try является в некотором смысле более читаемой и предполагает, что в большинстве случаев строка будет содержать действительное число. Но есть несколько вещей, которые вы можете рассмотреть с примерами, которые могут повлиять на ваш выбор.

  1. Как часто вы ожидаете, что строка не содержит действительного числа.
  2. Обратите внимание, что для массовой обработки вы должны создать объект Pattern вне цикла. Это остановит код от необходимости каждый раз перекомпилировать шаблон.
  3. Как правило, вы никогда не должны использовать ожидания в качестве логического потока. Ваш try - catch указывает логику, если это не строка, где, как ваше регулярное выражение указывает логику, если это число. Так что не было очевидно, каков контекст кода.
  4. Если вы выберете технику регулярных выражений, вам, вероятно, все равно придется конвертировать в какой-то момент, так что в действительности это может быть пустой тратой усилий.
  5. И, наконец, достаточно ли важны требования к производительности приложения для проведения анализа на этом уровне. Опять же, в общем, я бы рекомендовал сделать вещи максимально простыми, чтобы они работали, а затем, если есть проблемы с производительностью, используйте некоторые инструменты анализа кода, чтобы найти узкие места и настроить их.
4 голосов
/ 08 марта 2011
  1. Производительность: исключения являются медленными, как и логика на основе исключений, поэтому вторая будет быстрее.
  2. Техническое обслуживание / Надежность: первая является кристально чистой и будет обновляться с обновлениямиJava Framework.

При этом я лично предпочел бы первое.Производительность - это то, что вы хотите учитывать в своей архитектуре, дизайне структуры данных и т. Д., А не построчно.Измеряйте производительность и оптимизируйте то, что на самом деле медленно, а не то, что вы думаете, может быть медленным.

2 голосов
/ 08 марта 2011

Первый будет работать лучше, чем регулярное выражение, когда строка соответствует двойному. Например, он очень быстро анализируется, когда распознаватель жестко запрограммирован, как это было бы с Double.parse. Также нет ничего, что поддерживало бы то, что Java определяет как Double как строка. Не говоря уже о Double.parseDouble (), который легче читать.

Другое решение не будет скомпилировано, поэтому первое, что нужно сделать регулярному выражению, - это скомпилировать и проанализировать выражение регулярного выражения, затем запустить это выражение, затем вам нужно будет выполнить Double.parseDouble ( ), чтобы получить его в два раза. И это будет сделано для каждого числа, переданного ему. Вы можете оптимизировать его с помощью Pattern.compile (), но выполнение выражения будет выполняться медленнее. Особенно, когда вам нужно запустить Double.doubleParse, чтобы получить значение в double.

Да, исключения не являются супербыстрыми, но вам придется заплатить эту цену только при разборе ошибки. Если вы не планируете видеть много ошибок, то я не думаю, что вы заметите замедление сбора трассировки стека на броске (именно поэтому исключения работают плохо). Если вы столкнетесь только с несколькими исключениями, то производительность не будет проблемой. Проблема в том, что вы ожидаете удвоения, и, скорее всего, это не было какой-то ошибкой конфигурации, поэтому сообщите пользователю и выйдите из системы, либо выберите подходящее значение по умолчанию и продолжайте. Это все, что вы можете сделать в этих случаях.

1 голос
/ 08 марта 2011

Может быть, вы также можете попробовать этот способ. Но это является общим для строки, содержащей действительное число.

public static boolean isNumeric(String str) 
{ 
    str = "2.3452342323423424E8";
 //   str = "21414124.12412412412412";
 //   str = "123123";
    NumberFormat formatter = NumberFormat.getInstance(); 
    ParsePosition pos = new ParsePosition(0); 
    formatter.parse(str, pos); 
    return  str.length() == pos.getIndex();  
}
1 голос
/ 08 марта 2011

И да, я хотел бы знать, какая из них является хорошей практикой кодирования?

Либо может быть хорошая практика кодирования, в зависимости от контекста.

  • Если неверные числа маловероятны (т. Е. Это «исключительная» ситуация), то решение на основе исключений подходит.(Действительно, если вероятность неправильных чисел достаточно мала, исключения могут быть в среднем даже быстрее. Это зависит от относительной скорости Double.parseDouble() и скомпилированного регулярного выражения для типичных входных строк. Это должно быть измерено ...)

  • Если плохие числа достаточно (или очень) вероятны (т. Е. Это НЕ «исключительная» ситуация), то решение на основе регулярных выражений, вероятно, лучше.

  • Если путь к коду, который выполняет тест, выполняется редко, то на самом деле не имеет значения, какой подход вы используете.

1 голос
/ 08 марта 2011

Если вы используете parseDouble, вы в конечном итоге получите то, что сказал Марк, но в более удобочитаемом виде, и можете получить выгоду от повышения производительности и исправления ошибок.

Поскольку исключения генерируются дорого только тогда, когда они генерируются, нужно искать другую стратегию, только если вы

  • ожидаете, что неправильные форматы будут часто встречаться
  • ожидайтеони попадают в определенный шаблон, который вы можете поймать быстрее и заранее

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

Обратите внимание, что ваш шаблон отклоняет 7. как Double, в то время как Java и C / C ++ - нет, а также научную запись, такую ​​как 4.2e8.

0 голосов
/ 15 августа 2016

Ниже приведен тест производительности, чтобы увидеть разницу в производительности между регулярным выражением VS try catch для проверки строки является числовым.

В таблице ниже приведена статистика со списком (100 КБ) с тремя точками (90%, 70%, 50%) хороших данных (значение с плавающей запятой) и оставшимися плохими данными (строки).

                      **90% - 10%   70% - 30%   50% - 50%**
**Try Catch**           87234580    122297750   143470144
**Regular Expression**  202700266   192596610   162166308

Производительность try catch лучше (если количество неверных данных не превышает 50%), хотя try / catch может оказать некоторое влияние на производительность. Влияние на производительность try catch связано с тем, что try / catch не позволяет JVM выполнять некоторые оптимизации. Джошуа Блох в «Эффективной Яве» сказал следующее: Джошуа Блох в «Эффективной Яве» сказал следующее:

• Размещение кода внутри блока try-catch препятствует некоторым оптимизациям, которые в противном случае могли бы выполнить современные реализации JVM.

public class PerformanceStats {
static final String regularExpr = "([0-9]*[.])?[0-9]+";

public static void main(String[] args) {

    PerformanceStats ps = new PerformanceStats();
    ps.statsFinder();
    //System.out.println("123".matches(regularExpr));

}


private void statsFinder() {
    int count =  200000;
    int ncount = 200000;
    ArrayList<String> ar = getList(count, ncount);

    System.out.println("count = " + count + " ncount = " + ncount);

    long t1 = System.nanoTime();
    validateWithCatch(ar);
    long t2 = System.nanoTime();
    validateWithRegularExpression(ar);
    long t3 = System.nanoTime();

    System.out.println("time taken with Exception          " + (t2 - t1) );
    System.out.println("time taken with Regular Expression " + (t3 - t2) );
}


private ArrayList<String> getList(int count, int noiseCount) {
    Random rand = new Random();

    ArrayList<String> list = new ArrayList<String>();
    for (int i = 0; i < count; i++) {
        list.add((String) ("" + Math.abs(rand.nextFloat())));
    }
    // adding noise
    for (int i = 0; i < (noiseCount); i++) {
        list.add((String) ("sdss" + rand.nextInt() ));
    }
    return list;
}



private void validateWithRegularExpression(ArrayList<String> list) {
    ArrayList<Float> ar = new ArrayList<>();
    for (String s : list) {
        if (s.matches(regularExpr)) {
            ar.add(Float.parseFloat(s));
        }
    }
    System.out.println("the size is in regular expression " + ar.size());
}

private void validateWithCatch(ArrayList<String> list) {
    ArrayList<Float> ar = new ArrayList<>();
    for (String s : list) {
        try {
            float e = Float.parseFloat(s);
            ar.add(e);
        } catch (Exception e) {
        }
    }
    System.out.println("the size is in catch block " + ar.size());
}

}

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...