Тиражирование String.split с StringTokenizer

Вставка n элементов в приоритетную очередь будет иметь асимптотическую сложность O ( n log n ), поэтому с точки зрения сложности это не более эффективно, чем использование sort один раз, в конце.

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

5
задан Dani 12 June 2009 в 13:05
поделиться

8 ответов

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

Если это будет полезно, вы даже можете реализовать Iterable и получите расширенную поддержку цикла со строгой типизацией вместо поддержки Enumeration , предоставляемой StringTokenizer . Дайте мне знать, если вам нужна помощь в кодировании такого зверя - это действительно не должно быть слишком сложно.

Кроме того, я бы попробовал провести тесты производительности на ваших фактических данных, прежде чем слишком далеко отойти от существующего решения. Вы хоть представляете, сколько времени на выполнение на самом деле затрачено на String.split ? Я знаю, что вам нужно проанализировать много строк, но если вы впоследствии будете делать с ними что-нибудь значимое, я ожидаю, что это будет гораздо более значительным, чем разбиение.

Дайте мне знать, если вам нужна помощь в кодировании такого зверя - это действительно не должно быть слишком сложно.

Кроме того, я бы попробовал провести тесты производительности на ваших фактических данных, прежде чем слишком далеко отойти от существующего решения. Вы хоть представляете, сколько времени на выполнение на самом деле затрачено на String.split ? Я знаю, что вам нужно проанализировать много строк, но если вы впоследствии будете делать с ними что-нибудь значимое, я ожидаю, что это будет гораздо более значительным, чем разбиение.

Дайте мне знать, если вам нужна помощь в кодировании такого зверя - это действительно не должно быть слишком сложно.

Кроме того, я бы попробовал провести тесты производительности на ваших фактических данных, прежде чем слишком далеко отойти от существующего решения. Вы хоть представляете, сколько времени на выполнение на самом деле затрачено на String.split ? Я знаю, что вам нужно проанализировать много строк, но если вы впоследствии будете делать с ними что-нибудь значимое, я ожидаю, что это будет гораздо более значительным, чем разбиение.

12
ответ дан 18 December 2019 в 05:44
поделиться

Примечание. Проведя несколько быстрых тестов, Scanner оказался примерно в четыре раза медленнее, чем String.split. Следовательно, не используйте Scanner.

(Я оставляю сообщение, чтобы зафиксировать тот факт, что Scanner - плохая идея в данном случае. (Читать так: не голосуйте против меня за предложение Scanner, пожалуйста ...))

Предполагая, что вы используете Java 1.5 или выше, попробуйте Scanner , который реализует Iterator , как это происходит:

Scanner sc = new Scanner("dog,,cat");
sc.useDelimiter(",");
while (sc.hasNext()) {
    System.out.println(sc.next());
}

дает:

dog

cat
4
ответ дан 18 December 2019 в 05:44
поделиться

В зависимости от того, какие строки вам нужно токенизировать, вы можете написать свой собственный разделитель, например, на основе String.indexOf (). Вы также можете создать многоядерное решение для дальнейшего повышения производительности, так как токенизация строк не зависит друг от друга. Работайте с партиями, скажем, по 100 строк на ядро. Выполните String.split () или что-то еще.

2
ответ дан 18 December 2019 в 05:44
поделиться

После работы с классом StringTokenizer я мог не найти способ удовлетворить требования для возврата ["dog", "", "cat"] .

Кроме того, класс StringTokenizer оставлен только по соображениям совместимости, и допускается использование String.split . Из спецификации API для StringTokenizer :

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

Поскольку проблема заключается в предположительно низкой производительности метода String.split , нам нужно найти альтернативу.

Примечание: я говорю «предположительно низкая производительность», потому что это сложно чтобы определить, что каждый вариант использования приведет к тому, что StringTokenizer превосходит метод String.split . Более того, во многих случаях, если токенизация строк действительно не является узким местом приложения, определяемым правильным профилированием, я думаю, что в конечном итоге это будет преждевременной оптимизацией. Я был бы склонен сказать, что прежде чем приступить к оптимизации, напишите содержательный и простой для понимания код.

Теперь, исходя из текущих требований, вероятно, развернуть наш собственный токенизатор будет не так уж сложно.

Развернуть наш собственный токензатор!

Это простой токенизатор, который я написал. Я должен отметить, что здесь нет ни оптимизации скорости, ни проверки ошибок для предотвращения выхода за конец строки - это быстрая и грязная реализация:

class MyTokenizer implements Iterable<String>, Iterator<String> {
  String delim = ",";
  String s;
  int curIndex = 0;
  int nextIndex = 0;
  boolean nextIsLastToken = false;

  public MyTokenizer(String s, String delim) {
    this.s = s;
    this.delim = delim;
  }

  public Iterator<String> iterator() {
    return this;
  }

  public boolean hasNext() {
    nextIndex = s.indexOf(delim, curIndex);

    if (nextIsLastToken)
      return false;

    if (nextIndex == -1)
      nextIsLastToken = true;

    return true;
  }

  public String next() {
    if (nextIndex == -1)
      nextIndex = s.length();

    String token = s.substring(curIndex, nextIndex);
    curIndex = nextIndex + 1;

    return token;
  }

  public void remove() {
    throw new UnsupportedOperationException();
  }
}

MyTokenizer примет Строка для токенизации и Строка в качестве разделителя, а также использование метода String.indexOf для выполнения поиска разделителей. Токены создаются с помощью метода String.substring .

Я подозреваю, что можно было бы улучшить производительность, работая со строкой на уровне char [] , а не на уровне Строка уровня. Но я оставлю это читателю в качестве упражнения.

Класс также реализует Iterable и Iterator , чтобы использовать конструкцию цикла for-each , которая была введена в Java 5. StringTokenizer является перечислителем и не поддерживает конструкцию for-each .

Это быстрее?

Чтобы узнать, есть ли это вообще быстрее, я написал программу для сравнения скоростей следующими четырьмя методами:

  1. Использование StringTokenizer .
  2. Использование нового MyTokenizer .
  3. Использование ] String.split .
  4. Использование предварительно скомпилированного регулярного выражения в Pattern.compile .

В четырех методах строка «dog ,, cat» была разделены на токены. Хотя StringTokenizer включен в сравнение,

10
ответ дан 18 December 2019 в 05:44
поделиться

Вместо StringTokenizer вы можете попробовать класс StrTokenizer из Apache Commons Lang, который я цитирую:

Этот класс может разбивать String на множество меньших строк. Он нацелен на выполнение той же работы, что и StringTokenizer, однако он предлагает гораздо больший контроль и гибкость, включая реализацию интерфейса ListIterator.

Пустые токены могут быть удалены или возвращены как пустые.

Думаю, это похоже на то, что вам нужно?

2
ответ дан 18 December 2019 в 05:44
поделиться

Вы можете сделать что-то подобное. Он не идеален, но может сработать для вас.

public static List<String> find(String test, char c) {
    List<String> list = new Vector<String>();
    start;
    int i=0;
    while (i<=test.length()) {
        int start = i;
        while (i<test.length() && test.charAt(i)!=c) {
            i++;
        }
        list.add(test.substring(start, i));
        i++;
    }
    return list;
}

Если возможно, вы можете опустить элемент List и напрямую сделать что-то с подстрокой:

public static void split(String test, char c) {
    int i=0;
    while (i<=test.length()) {
        int start = i;
        while (i<test.length() && test.charAt(i)!=c) {
            i++;
        }
        String s = test.substring(start,i);
         // do something with the string here
        i++;
    }
}

В моей системе последний метод быстрее, чем решение StringTokenizer, но вы может захотеть проверить, как это работает для вас. (Конечно, вы могли бы сделать этот метод немного короче, опуская {} второго вида while, и, конечно, вы могли бы использовать цикл for вместо внешнего цикла while и включить в него последний i ++, но я этого не сделал. Я не делаю это здесь, потому что считаю это плохим стилем.

1
ответ дан 18 December 2019 в 05:44
поделиться

Если ваш ввод структурирован, вы можете взглянуть на компилятор JavaCC. Он генерирует java-класс, читающий ваш ввод. Это будет выглядеть так:

TOKEN { <CAT: "cat"> , <DOG:"gog"> }

input: (cat() | dog())*


cat: <CAT>
   {
   animals.add(new Animal("Cat"));
   }

dog: <DOG>
   {
   animals.add(new Animal("Dog"));
   }
-1
ответ дан 18 December 2019 в 05:44
поделиться

Ну, самое быстрое, что вы могли бы сделать, - это вручную пройти по строке, например

List<String> split(String s) {
        List<String> out= new ArrayList<String>();
           int idx = 0;
           int next = 0;
        while ( (next = s.indexOf( ',', idx )) > -1 ) {
            out.add( s.substring( idx, next ) );
            idx = next + 1;
        }
        if ( idx < s.length() ) {
            out.add( s.substring( idx ) );
        }
               return out;
    }

Этот (неформальный тест) выглядит примерно в два раза быстрее, чем split. Однако повторять этот способ немного опасно, например, он сломается на экранированных запятых, и если вам в какой-то момент придется с этим справиться (потому что ваш список из миллиарда строк имеет 3 экранированные запятые) к тому времени, когда вы Если вы допустили это, вы, вероятно, в конечном итоге потеряете часть выигрыша в скорости.

В конечном итоге, вероятно, не стоит беспокоиться.

0
ответ дан 18 December 2019 в 05:44
поделиться
Другие вопросы по тегам:

Похожие вопросы: