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

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

Ответ 1

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

  • 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().

Ответ 2

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

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

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

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

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

Scanner больше похож на поток:

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

или

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

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

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

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

Ответ 3

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

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

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

Ответ 4

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

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

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

Ответ 5

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

Ответ 6

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

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

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

Ответ 7

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

Ответ 8

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

public static ArrayList<String> splitBySingleChar(final char[] s,
        final char splitChar) {
    final ArrayList<String> result = new ArrayList<String>();
    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(), чтобы получить массив char для строки. Например:

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

Ответ 9

Одно важное отличие состоит в том, что как 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.

Ответ 10

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

ABC | IJK