Статьи

Разделитель гуавы против StringUtils

Поэтому недавно я написал пост о старом добром надежном Apache Commons StringUtils , который вызвал пару комментариев, один из которых заключался в том, что Google Guava предоставляет улучшенные механизмы для объединения и разделения строк. Я должен признать, что это угол Гуавы, который мне еще предстоит изучить. Поэтому я подумал, что мне следует присмотреться и сравнить со StringUtils, и я должен признать, что был удивлен тем, что нашел.

Расщепление струн а? Там не может быть много разных способов сделать это, конечно?

Ну, Guava и StringUtils используют систолически иной подход. Давайте начнем с основного использования.

1
2
3
4
5
// Apache StringUtils...
String[] tokens1 = StringUtils.split('one,two,three',',');
 
// Guava splitter...
Iterable<String> tokens2 = Splitter.on(',').split('one,two,three');

Итак, мое первое наблюдение заключается в том, что Splitter больше ориентирован на объект. Вы должны создать объект разделителя, который затем будете использовать для разделения. В то время как методы разделителя StringUtils используют более функциональный стиль со статическими методами.

Здесь я предпочитаю Splitter. Нужен многоразовый разделитель, разделяющий запятые? Сплиттер, который также обрезает начальные и конечные пробелы и игнорирует пустые элементы? Не проблема:

1
2
3
4
5
6
Splitter niceCommaSplitter = Splitter.on(',')
                              .omitEmptyString()
                              .trimResults();
 
niceCommaSplitter.split('one,, two,  three'); //'one','two','three'
niceCommaSplitter.split('  four  ,  five  '); //'four','five'

Это выглядит действительно полезным, есть ли другие отличия?

StringUtils.split отметить, что Splitter возвращает Iterable<String> , тогда как StringUtils.split возвращает массив String.

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

Я также не думал, что это имеет большое значение, пока я не изучил эффективность двух подходов. Для этого я попытался запустить следующий код:

01
02
03
04
05
06
07
08
09
10
11
12
13
final String numberList = 'One,Two,Three,Four,Five,Six,Seven,Eight,Nine,Ten';
 
long start = System.currentTimeMillis(); 
for(int i=0; i<1000000; i++) {
    StringUtils.split(numberList , ',');  
}
System.out.println(System.currentTimeMillis() - start);
 
start = System.currentTimeMillis();
for(int i=0; i<1000000; i++) {
    Splitter.on(',').split(numberList );
}
System.out.println(System.currentTimeMillis() - start);

На моей машине этот вывод следующий раз:

594
31

Разветвитель гуавы почти в 10 раз быстрее!

Теперь это намного большая разница, чем я ожидал, Splitter более чем в 10 раз быстрее, чем StringUtils. Как это может быть? Ну, я подозреваю, что это как-то связано с типом возврата. Splitter возвращает Iterable<String> , тогда как StringUtils.split предоставляет вам массив строк! Так что Splitter самом деле не нужно создавать новые объекты String .

Также стоит отметить, что вы можете кэшировать свой объект Splitter, что приводит к еще более быстрому выполнению.

Блими, конец спора? Сплиттер Гуавы побеждает каждый раз?

Держать на секунду. Это не совсем полная история. Заметьте, мы на самом деле ничего не делаем с результатом строк? Как я уже говорил, похоже, что Splitter фактически не создает никаких новых строк. Я подозреваю, что это на самом деле откладывает это до объекта Iterator, который он возвращает.

Так можем ли мы проверить это?

Конечно, вещь. Вот некоторый код для повторной проверки длины сгенерированных подстрок:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
final String numberList = 'One,Two,Three,Four,Five,Six,Seven,Eight,Nine,Ten';
long start = System.currentTimeMillis(); 
for(int i=0; i<1000000; i++) {
  final String[] numbers = StringUtils.split(numberList, ',');
    for(String number : numbers) {
      number.length();
    }
  }
System.out.println(System.currentTimeMillis() - start);
 
Splitter splitter = Splitter.on(',');
start = System.currentTimeMillis();
for(int i=0; i<1000000; i++) {
  Iterable<String> numbers = splitter.split(numberList);
    for(String number : numbers) {
      number.length();
    }
  }
System.out.println(System.currentTimeMillis() - start);

На моей машине это выводит:

609
2048

Разветвитель гуавы почти в 4 раза медленнее!

Действительно, я ожидал, что они будут примерно одинаковыми, или, может быть, Гуава немного быстрее, так что это еще один удивительный результат. Похоже, возвращая Iterable, Splitter торгует немедленной прибылью для более долгосрочной боли. Здесь также есть мораль о том, что тесты производительности на самом деле проверяют что-то полезное.

В заключение я думаю, что я все еще буду использовать Splitter большую часть времени. В небольших списках разница в производительности будет незначительной, и Splitter просто чувствует себя намного приятнее в использовании. Тем не менее, я был удивлен результатом, и если вы разделяете много строк и производительность является проблемой, возможно, стоит подумать о том, чтобы вернуться к Commons StringUtils.

Ссылка: Guava Splitter vs StringUtils от нашего партнера JCG Тома Джеффериса в блоге Tom’s Programming Blog .