Подтвердить что ты не робот

Производительность класса StringTokenizer против метода String.split в Java

В моем программном обеспечении мне нужно разделить строку на слова. В настоящее время у меня более 19 000 000 документов с более чем 30 словами.

Какой из следующих двух способов - лучший способ сделать это (с точки зрения производительности)?

StringTokenizer sTokenize = new StringTokenizer(s," ");
while (sTokenize.hasMoreTokens()) {

или

String[] splitS = s.split(" ");
for(int i =0; i < splitS.length; i++)
4b9b3361

Ответ 1

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

Однако получение данных из базы данных по-прежнему будет намного дороже.

StringBuilder sb = new StringBuilder();
for (int i = 100000; i < 100000 + 60; i++)
    sb.append(i).append(' ');
String sample = sb.toString();

int runs = 100000;
for (int i = 0; i < 5; i++) {
    {
        long start = System.nanoTime();
        for (int r = 0; r < runs; r++) {
            StringTokenizer st = new StringTokenizer(sample);
            List<String> list = new ArrayList<String>();
            while (st.hasMoreTokens())
                list.add(st.nextToken());
        }
        long time = System.nanoTime() - start;
        System.out.printf("StringTokenizer took an average of %.1f us%n", time / runs / 1000.0);
    }
    {
        long start = System.nanoTime();
        Pattern spacePattern = Pattern.compile(" ");
        for (int r = 0; r < runs; r++) {
            List<String> list = Arrays.asList(spacePattern.split(sample, 0));
        }
        long time = System.nanoTime() - start;
        System.out.printf("Pattern.split took an average of %.1f us%n", time / runs / 1000.0);
    }
    {
        long start = System.nanoTime();
        for (int r = 0; r < runs; r++) {
            List<String> list = new ArrayList<String>();
            int pos = 0, end;
            while ((end = sample.indexOf(' ', pos)) >= 0) {
                list.add(sample.substring(pos, end));
                pos = end + 1;
            }
        }
        long time = System.nanoTime() - start;
        System.out.printf("indexOf loop took an average of %.1f us%n", time / runs / 1000.0);
    }
 }

печатает

StringTokenizer took an average of 5.8 us
Pattern.split took an average of 4.8 us
indexOf loop took an average of 1.8 us
StringTokenizer took an average of 4.9 us
Pattern.split took an average of 3.7 us
indexOf loop took an average of 1.7 us
StringTokenizer took an average of 5.2 us
Pattern.split took an average of 3.9 us
indexOf loop took an average of 1.8 us
StringTokenizer took an average of 5.1 us
Pattern.split took an average of 4.1 us
indexOf loop took an average of 1.6 us
StringTokenizer took an average of 5.0 us
Pattern.split took an average of 3.8 us
indexOf loop took an average of 1.6 us

Стоимость открытия файла составит около 8 мс. Поскольку файлы настолько малы, ваш кеш может повысить производительность в 2-5 раз. Тем не менее, он собирается потратить ~ 10 часов на открытие файлов. Стоимость использования split vs StringTokenizer намного меньше 0,01 мс каждый. Чтобы проанализировать 19 миллионов x 30 слов * 8 букв в слове должны занимать около 10 секунд (примерно 1 ГБ за 2 секунды)

Если вы хотите повысить производительность, я предлагаю вам иметь гораздо меньше файлов. например используйте базу данных. Если вы не хотите использовать базу данных SQL, я предлагаю использовать один из этих http://nosql-database.org/

Ответ 2

Сплит в Java 7 просто вызывает indexOf для этого входа, см. источник. Сплит должен быть очень быстрым, близко к повторным вызовам indexOf.

Ответ 3

В спецификации Java API рекомендуется использовать split. См. Документацию StringTokenizer.

Ответ 4

Еще одна важная вещь, недокументированная, насколько я заметил, заключается в том, что запрос StringTokenizer на возврат разделителей вместе с токенированной строкой (с помощью конструктора StringTokenizer(String str, String delim, boolean returnDelims)) также сокращает время обработки. Итак, если вы ищете производительность, я бы рекомендовал использовать что-то вроде:

private static final String DELIM = "#";

public void splitIt(String input) {
    StringTokenizer st = new StringTokenizer(input, DELIM, true);
    while (st.hasMoreTokens()) {
        String next = getNext(st);
        System.out.println(next);
    }
}

private String getNext(StringTokenizer st){  
    String value = st.nextToken();
    if (DELIM.equals(value))  
        value = null;  
    else if (st.hasMoreTokens())  
        st.nextToken();  
    return value;  
}

Несмотря на накладные расходы, введенные методом getNext(), который отбрасывает разделители для вас, он по-прежнему на 50% быстрее в соответствии с моими показателями.

Ответ 5

Используйте split.

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

Ответ 6

Что там должны сделать 19 000 000 документов? Вам нужно регулярно разглашать слова во всех документах? Или это проблема с одним выстрелом?

Если вы показываете/запрашиваете один документ за раз, всего 30 слов, это настолько крошечная проблема, что любой метод будет работать.

Если вам нужно обрабатывать все документы одновременно, всего 30 слов, это настолько крошечная проблема, что вы все равно будете связаны с IO.

Ответ 7

Независимо от его устаревшего статуса, я ожидал бы, что StringTokenizer будет значительно быстрее, чем String.split() для этой задачи, поскольку он не использует регулярные выражения: он просто сканирует вход напрямую, так же, как вы сами через indexOf(). На самом деле String.split() приходится скомпилировать регулярное выражение каждый раз, когда вы его вызываете, поэтому оно не так эффективно, как прямое использование регулярного выражения непосредственно.

Ответ 8

При запуске тестов micro (и в этом случае, даже nano) есть много, которые влияют на ваши результаты. Оптимизация JIT и сборка мусора, чтобы назвать лишь некоторые из них.

Чтобы получить значимые результаты из микро-тестов, просмотрите библиотеку jmh. В нем есть отличные примеры, связанные с тем, как использовать хорошие тесты.

Ответ 9

Это может быть разумный бенчмаркинг с использованием 1.6.0

http://www.javamex.com/tutorials/regular_expressions/splitting_tokenisation_performance.shtml#.V6-CZvnhCM8

Ответ 10

Эффективный мудрый StringTokeniser лучше, чем раскол. Проверьте код ниже,

введите описание изображения здесь

Но в соответствии с документами Java его использование не рекомендуется. Проверьте Здесь