Сканер против StringTokenizer против String.Split

Я только что узнал о classе Java Scanner, и теперь мне интересно, как он сравнивает / конкурирует с StringTokenizer и String.Split. Я знаю, что StringTokenizer и String.Split работают только с Strings, так почему я должен использовать Scanner для String? Сканер просто предназначен для одноразового шоппинга?

Это, по сути, лошади для курсов.

  • Scanner предназначен для случаев, когда вам нужно разбирать строку, вытягивая данные разных типов. Он очень гибкий, но, возможно, не дает вам простейшего API для простого получения массива строк, ограниченных определенным выражением.
  • String.split() и Pattern.split() дают вам простой синтаксис для выполнения последнего, но это по существу все, что они делают. Если вы хотите проанализировать полученные строки или изменить разделитель на полпути в зависимости от конкретного токена, они не помогут вам в этом.
  • StringTokenizer является еще более ограничивающим, чем String.split() , а также немного запутанным для использования. Он по существу предназначен для вытягивания жетонов, ограниченных фиксированными подстроками. Из-за этого ограничения он примерно в два раза быстрее, чем String.split() . (См. Мои сравнения String.split() и StringTokenizer .) Он также предшествует API регулярных выражений, частью которого является String.split() .

Вы можете отметить из моих таймингов, что String.split() все еще может токенизировать тысячи строк за несколько миллисекунд на типичной машине. Кроме того, у него есть преимущество перед StringTokenizer что он дает вам результат как строковый массив, который обычно вы хотите. Использование Enumeration , предоставляемое StringTokenizer , в большинстве случаев слишком синтаксически суетливое. С этой точки зрения, StringTokenizer настоящее время является пустой тратой пространства, и вы можете просто использовать String.split() .

Начнем с устранения StringTokenizer . Он стареет и даже не поддерживает регулярные выражения. В его документации указано:

StringTokenizer – это унаследованный class, который сохраняется по соображениям совместимости, хотя его использование не рекомендуется в новом коде. Рекомендуется, чтобы любой, кто ищет эту функциональность, использовал метод split для String или пакет java.util.regex .

Так что давайте сразу выбросим его. Это оставляет split() и Scanner . В чем разница между ними?

С одной стороны, split() просто возвращает массив, что упрощает использование цикла foreach:

 for (String token : input.split("\\s+") { ... } 

Scanner построен скорее как stream:

 while (myScanner.hasNext()) { String token = myScanner.next(); ... } 

или

 while (myScanner.hasNextDouble()) { double token = myScanner.nextDouble(); ... } 

(Он имеет довольно большой API , поэтому не думайте, что он всегда ограничен такими простыми вещами.)

Этот интерфейс в стиле streamа может быть полезен для parsingа простых текстовых файлов или ввода в консоль, когда у вас нет (или не может получить) все входные данные перед началом анализа.

Лично, единственный раз, когда я могу вспомнить использование Scanner это касается школьных проектов, когда мне приходилось вводить пользовательский ввод из командной строки. Это облегчает такую ​​работу. Но если у меня есть String которую я хочу разделить, почти нет проблем с split() .

StringTokenizer всегда был там. Это самый быстрый из всех, но подобная переписи идиома может выглядеть не так элегантно, как другие.

раскол появился на JDK 1.4. Медленно, чем токенизатор, но проще в использовании, поскольку он может быть вызван из classа String.

Сканер оказался на JDK 1.5. Он является наиболее гибким и заполняет давно существующий пробел в Java API для поддержки эквивалента известного семейства функций Cs scanf.

Сплит медленный, но не такой медленный, как сканер. StringTokenizer быстрее, чем раскол. Тем не менее, я обнаружил, что могу получить удвоенную скорость, используя некоторую гибкость, чтобы получить ускорение, которое я сделал на JFastParser https://github.com/hughperkins/jfastparser

Тестирование на строке, содержащей один миллион удвоений:

 Scanner: 10642 ms Split: 715 ms StringTokenizer: 544ms JFastParser: 290ms 

Если у вас есть объект String, который вы хотите использовать для токенизации, используйте метод разделения String поверх StringTokenizer. Если вы анализируете текстовые данные из источника, находящегося за пределами вашей программы, например, из файла или из пользователя, это то, где сканер подходит.

String.split кажется намного медленнее, чем StringTokenizer. Единственным преимуществом с split является то, что вы получаете массив токенов. Также вы можете использовать любые регулярные выражения в split. org.apache.commons.lang.StringUtils имеет метод split, который работает намного быстрее, чем любой из двух. StringTokenizer или String.split. Но использование ЦП для всех трех почти одинаково. Поэтому нам также нужен метод, который не требует интенсивного процессора, которого я все еще не могу найти.

Недавно я сделал несколько экспериментов по поводу плохой производительности String.split () в ситуациях с высокой степенью производительности. Вы можете найти это полезным.

http://eblog.chrononsystems.com/hidden-evils-of-javas-stringsplit-and-stringr

Суть в том, что String.split () каждый раз скомпилирует шаблон регулярного выражения и, таким образом, может замедлить работу вашей программы по сравнению с тем, если вы используете объект с предварительно скомпилированным шаблоном и используете его напрямую для работы с String.

Для сценариев по умолчанию я бы предложил Pattern.split (), но если вам нужна максимальная производительность (особенно на Android, все решения, которые я тестировал, довольно медленные), и вам нужно всего лишь разделить на один символ, теперь я использую свой собственный метод:

 public static ArrayList splitBySingleChar(final char[] s, final char splitChar) { final ArrayList result = new ArrayList(); final int length = s.length; int offset = 0; int count = 0; for (int i = 0; i < length; i++) { if (s[i] == splitChar) { if (count > 0) { result.add(new String(s, offset, count)); } offset = i + 1; count = 0; } else { count++; } } if (count > 0) { result.add(new String(s, offset, count)); } return result; } 

Используйте «abc» .toCharArray (), чтобы получить массив символов для строки. Например:

 String s = " a bb ccc dddd eeeee ffffff ggggggg "; ArrayList result = splitBySingleChar(s.toCharArray(), ' '); 

Важным отличием является то, что как String.split (), так и Scanner могут создавать пустые строки, но StringTokenizer этого не делает.

Например:

 String str = "ab cd ef"; StringTokenizer st = new StringTokenizer(str, " "); for (int i = 0; st.hasMoreTokens(); i++) System.out.println("#" + i + ": " + st.nextToken()); String[] split = str.split(" "); for (int i = 0; i < split.length; i++) System.out.println("#" + i + ": " + split[i]); Scanner sc = new Scanner(str).useDelimiter(" "); for (int i = 0; sc.hasNext(); i++) System.out.println("#" + i + ": " + sc.next()); 

Вывод:

 //StringTokenizer #0: ab #1: cd #2: ef //String.split() #0: ab #1: cd #2: #3: ef //Scanner #0: ab #1: cd #2: #3: ef 

Это связано с тем, что разделитель для String.split () и Scanner.useDelimiter () - это не просто строка, а регулярное выражение. Мы можем заменить разделитель "" на "+" в приведенном выше примере, чтобы заставить их вести себя как StringTokenizer.

String.split () работает очень хорошо, но имеет свои собственные границы, например, если вы хотите разбить строку, как показано ниже, на основе символа single или double pipe (|), это не сработает. В этой ситуации вы можете использовать StringTokenizer.

ABC | IJK

  • Вложенная функция функции strtok в C
  • Как лучше всего разбить строки csv в oracle 9i
  • Давайте будем гением компьютера.