#java #соревнование
Часто приходится пАрсить строку в целое число, типа: public static int string2Int(String s, int defaultValue) { int i=defaultValue; try { i=Integer.parseInt(s); } catch(Exception ex) { } return i; } Код хорош всем, за исключением, того, что Exception, как известно, дорогой процесс. Так вот вопрос: кто предложит наиболее эффективный (в смысле ресурсов и скорости) способ парсинга строки в int? Строка содержит число в десятичном исчислении (никаких hex/bin/octal) - возможно и отрицательное. Если строка содержит дробное число, то дробная часть откидывается. Условия: метод принимает 2 параметра: строка и значение по умолчанию - никаких Exception не выкидывается. При ошибке парсинга возвращается значение по умолчанию. Update В итоге есть 3 способа, условно классический (в самом вопросе), метод прямого парсинга в инкарнации @АртемКоновалов и удаленный способ через Apache Commons предложенный @LiashenkoV. Я составил небольшой тест, который генерирует 50 тыс. случайных строк, 1/3 строка типа int, 1/3 строка double, 1/3 строка портится внесением случайных символов. Получились следующие цифры: ClassicParser - 300 мс. ArtemParser - 100 мс. ApacheParser - 200 мс.
Ответы
Ответ 1
Получилось такое решение: public static long getNumber(String value, long defaultValue) { if (value == null) return defaultValue; value = value.trim(); if (value.length() == 0) return defaultValue; final char[] chars = value.toCharArray(); final char delimiter = DecimalFormatSymbols.getInstance().getDecimalSeparator(); final boolean isNegative; if (chars[0] == '-' || chars[0] == '+') { if (chars.length == 1 || chars[1] == delimiter) return defaultValue; else isNegative = chars[0] == '-'; } else if (isDigit(chars[0])) isNegative = false; else return defaultValue; final int radix = 10; boolean isFloat = false; long result = 0; for (int i = isDigit(chars[0]) ? 0 : 1; i < chars.length; i++) { char currentSymbol = chars[i]; if (currentSymbol == delimiter) if (isFloat) return defaultValue; else isFloat = true; if (currentSymbol != delimiter && !isDigit(currentSymbol)) return defaultValue; if (!isFloat) { long intermediateResult = result * radix + parse(currentSymbol); if (isOverflow(result, intermediateResult)) return defaultValue; else result = intermediateResult; } } return isNegative ? -result : result; } private static boolean isOverflow(long prev, long current) { return prev != 0 && Math.signum(prev) != Math.signum(current); } private static boolean isDigit(char symbol) { return '0' <= symbol && symbol <= '9'; } private static int parse(char symbol) { return symbol - '0'; } Тест: @Test public void test() { final int defaultValue = 10; assertEquals(0, getNumber("0", 10)); assertEquals(1, getNumber("1", 10)); assertEquals(-1, getNumber("-1", 10)); assertEquals(420, getNumber("0420", defaultValue)); assertEquals(142, getNumber("142", defaultValue)); assertEquals(-72, getNumber("-72", defaultValue)); assertEquals(19, getNumber("+19", defaultValue)); assertEquals(-19, getNumber("-19.11", defaultValue)); assertEquals(19, getNumber("+19.16", defaultValue)); assertEquals(defaultValue, getNumber("+19+.16", defaultValue)); assertEquals(defaultValue, getNumber("+1.9+16", defaultValue)); assertEquals(defaultValue, getNumber("1.1.16", defaultValue)); assertEquals(defaultValue, getNumber("+.", defaultValue)); assertEquals(defaultValue, getNumber(".", defaultValue)); assertEquals(defaultValue, getNumber("+.0", defaultValue)); assertEquals(defaultValue, getNumber(Long.MAX_VALUE + "0", 10)); assertEquals(defaultValue, getNumber(Long.MIN_VALUE + "0", 10)); }
Комментариев нет:
Отправить комментарий