java — Как определить, является ли строка числом?

В связи с повышенным интересом по данному вопросу, сделал маленькое исследование 🙂

Полный код доступен тут.

В исследовании принимали участия классы:

CharacterDelegator() // код @Sergey
ComplexMatcher() // код @VladD
NumberUtilsDelegator() // мой код
SimpleMatcher()  // код @ivkremer (для простых чисел)
GuavaDelegator()  // код @Nofate♦
SimpleMatcherWithDot() // код @ivkremer (для чисел с точкой)
SimpleParser()  // оригинальный код от @pol
GuavaComplexDelegator() // модифицированный вариант кода @Nofate♦ для Float
InnerSetImpl()  // мой специфический вариант

(@VladD, извиняюсь, понимаю, что делает ваш регэксп, но завести его у меня не получилось)

Тестовые данные:

{"1", "1.1", "1.11", "1.0", "-1",
 "-1.0", "-1.1", "4563575687468353456", 
 "1l", "1L", "1f", "1F", "0xCAFEBABE", "1F",
 "10000000000000000000000000000000000000000000000000", 
 "1.26767E108"}

Вывода много, поэтому вкратце:

  • абсолютно все строки из примера распарсил только NumberUtils (commons-lang)
  • вариант с использованием Guava (расширенный) справился со всем кроме long-нотаций («l») и «0xCAFEBABE» 🙂 (честно, не понимаю, почему он варианты с «f»-нотацией прожевал)
  • остальные варианты в большей степени рассчитаны на парсинг именно интов, хотя в изначальном вопросе об этом ни слова 🙂

А вот самое интересное — это время работы данного кода.

Start performance test for core.impl.CharacterDelegator Ints: 125ms Numbers: 67ms Numbers with 25% errors: 50ms Small Ints: 43ms

Start performance test for core.impl.ComplexMatcher Ints: 10825ms Numbers: 11134ms Numbers with 25% errors: 10606ms Small Ints: 10380ms

Start performance test for core.impl.InnerSetImpl Ints: 50ms Numbers: 52ms Numbers with 25% errors: 54ms Small Ints: 42ms

Start performance test for core.impl.NumberUtilsDelegator Ints: 111ms Numbers: 91ms Numbers with 25% errors: 99ms Small Ints: 51ms

Start performance test for core.impl.SimpleMatcher Ints: 1072ms Numbers: 853ms Numbers with 25% errors: 847ms Small Ints: 766ms

Start performance test for core.impl.GuavaDelegator Ints: 131ms Numbers: 108ms Numbers with 25% errors: 124ms Small Ints: 119ms

Start performance test for core.impl.SimpleMatcherWithDot Ints: 3069ms Numbers: 5855ms Numbers with 25% errors: 5484ms Small Ints: 2548ms

Start performance test for core.

impl.SimpleParser Ints: 157ms Numbers: 2189ms Numbers with 25% errors: 2117ms Small Ints: 81ms

Start performance test for core.impl.GuavaComplexDelegator Ints: 980ms Numbers: 943ms Numbers with 25% errors: 1016ms Small Ints: 837ms

Тест построен следующим образом

  1. Генерируем 2 рандомных списка со стрингами (числа). Тут есть 4 варианта:

      1. Просто Инты.
      2. Числа (с 50% вероятностью в стринге есть точка).
      3. Числа с возможной ошибкой (25% что в строке есть подстрока "error").
      4. Набор Интов в небольшом диапазоне.
    
  2. Делаем тестовый прогон на 10_000 элементах.

  3. Делаем прогон на 1_000_000 элементах.

Из приведенных выкладок видно, что NumberUtils работает быстрее всего. Схожее время работы у простого варианты Guava`ы и простых regexp. Даже добавление простой точки значительно замедляет код. Также замечу, что код @Sergey работает очень быстро, но он рассчитан на проверку строго интов.

А еще есть мой специфический пример InnerSetImpl. Он основан на допущении, что у нас есть ограниченное число возможных вариантов (то есть, мы можем и готовы держать их в памяти). Тогда мы просто помещаем их в HashSet и проверяем наличие строк в нем. Собственно, такой вариант самый быстрый, но допущение многое портит 🙂

ИТОГО: если нужно простое и элегантное решение — то лучше всего воспользоваться NumberUtils.isNumber(str) и не париться.

А если вдруг у вас стоит специфическая задача на парсинг не просто чисел, но чисел в Java-нотациях, то это единственный полностью рабочий вариант.

(Все вышесказанное относится только к приведенному коду. Я не исключаю, что можно придумать (или даже существует) более правильное или более быстрое решение).

Spark Преобразование строкового типа в целочисленный тип (int)

  • Автор сообщения: