Перейти к основному содержимому

Альтернативы L-Trim и R-Trim в Java

· 4 мин. чтения

1. Обзор

Метод String.trim() удаляет конечные и начальные пробелы. Но простое выполнение L-Trim или R-Trim не поддерживается.

В этом уроке мы увидим несколько способов реализации этого; в конце мы сравним их производительность.

2. пока Цикл

Самое простое решение — пройтись по строке, используя пару циклов while .

Для L-Trim мы будем читать строку слева направо, пока не столкнемся с непробельным символом:

int i = 0;
while (i < s.length() && Character.isWhitespace(s.charAt(i))) {
i++;
}
String ltrim = s.substring(i);

ltrim — это подстрока, начинающаяся с первого непробельного символа.

Или для R-Trim мы будем читать нашу строку справа налево, пока не столкнемся с непробельным символом:

int i = s.length()-1;
while (i >= 0 && Character.isWhitespace(s.charAt(i))) {
i--;
}
String rtrim = s.substring(0,i+1);

rtrim — это подстрока, начинающаяся с начала и заканчивающаяся первым непробельным символом.

3. String.replaceAll с использованием регулярных выражений

Другой вариант — использовать String.replaceAll() и регулярное выражение:

String ltrim = src.replaceAll("^\\s+", "");
String rtrim = src.replaceAll("\\s+$", "");

(\s+) — это регулярное выражение, которое соответствует одному или нескольким пробельным символам. Знаки вставки (^) и ($) в начале и в конце регулярного выражения соответствуют началу и концу строки.

4. Pattern.compile() и .matcher()

Мы также можем повторно использовать регулярные выражения с java.util.regex.Pattern :

private static Pattern LTRIM = Pattern.compile("^\\s+");
private static Pattern RTRIM = Pattern.compile("\\s+$");

String ltrim = LTRIM.matcher(s).replaceAll("");
String rtim = RTRIM.matcher(s).replaceAll("");

5. Апач Коммонс

Кроме того, мы можем воспользоваться методами Apache Commons StringUtils#stripStart и #stripEnd для удаления пробелов.

Для этого давайте сначала добавим зависимость commons -lang3 :

<dependency> 
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>

Следуя документации, мы используем null для удаления пробелов:

String ltrim = StringUtils.stripStart(src, null);
String rtrim = StringUtils.stripEnd(src, null);

6. Гуава

Наконец, мы воспользуемся преимуществами методов Guava CharMatcher#trimLeadingFrom и #trimTrailingFrom , чтобы получить тот же результат.

Снова добавим соответствующую зависимость Maven, на этот раз guava :

<dependency> 
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>31.0.1-jre</version>
</dependency>

А в Guava это очень похоже на то, как это делается в Apache Commons, только с более целенаправленными методами:

String ltrim = CharMatcher.whitespace().trimLeadingFrom(s); 
String rtrim = CharMatcher.whitespace().trimTrailingFrom(s);

7. Сравнение производительности

Посмотрим на эффективность методов. Как обычно, мы будем использовать фреймворк с открытым исходным кодом Java Microbenchmark Harness (JMH) для сравнения различных альтернатив за наносекунды.

7.1. Настройка эталона

Для первоначальной конфигурации бенчмарка мы использовали пять форков и расчет среднего времени в наносекундах:

@Fork(5)
@State(Scope.Benchmark)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)

В методе настройки мы инициализируем исходное поле сообщения и результирующую строку для сравнения:

@Setup
public void setup() {
src = " White spaces left and right ";
ltrimResult = "White spaces left and right ";
rtrimResult = " White spaces left and right";
}

Все тесты сначала удаляют левые пробелы, затем удаляют правые пробелы и, наконец, сравнивают полученные результаты с ожидаемыми строками.

7.2. пока цикл

Для нашего первого теста давайте воспользуемся циклом while :

@Benchmark
public boolean whileCharacters() {
String ltrim = whileLtrim(src);
String rtrim = whileRtrim(src);
return checkStrings(ltrim, rtrim);
}

7.3. String.replaceAll() с регулярным выражением

Затем попробуем String.replaceAll() :

@Benchmark
public boolean replaceAllRegularExpression() {
String ltrim = src.replaceAll("^\\s+", "");
String rtrim = src.replaceAll("\\s+$", "");
return checkStrings(ltrim, rtrim);
}

7.4. Pattern.compile().matches()

После этого идет Pattern.compile().matches() :

@Benchmark
public boolean patternMatchesLTtrimRTrim() {
String ltrim = patternLtrim(src);
String rtrim = patternRtrim(src);
return checkStrings(ltrim, rtrim);
}

7.5. Апач Коммонс

В-четвертых, Apache Commons:

@Benchmark
public boolean apacheCommonsStringUtils() {
String ltrim = StringUtils.stripStart(src, " ");
String rtrim = StringUtils.stripEnd(src, " ");
return checkStrings(ltrim, rtrim);
}

7.6. Гуава

И, наконец, давайте использовать гуаву:

@Benchmark
public boolean guavaCharMatcher() {
String ltrim = CharMatcher.whitespace().trimLeadingFrom(src);
String rtrim = CharMatcher.whitespace().trimTrailingFrom(src);
return checkStrings(ltrim, rtrim);
}

7.7. Анализ результатов

И мы должны получить некоторые результаты, похожие на следующие:

# Run complete. Total time: 00:16:57

Benchmark Mode Cnt Score Error Units
LTrimRTrim.apacheCommonsStringUtils avgt 100 108,718 ± 4,503 ns/op
LTrimRTrim.guavaCharMatcher avgt 100 113,601 ± 5,563 ns/op
LTrimRTrim.patternMatchesLTtrimRTrim avgt 100 850,085 ± 17,578 ns/op
LTrimRTrim.replaceAllRegularExpression avgt 100 1046,660 ± 7,151 ns/op
LTrimRTrim.whileCharacters avgt 100 110,379 ± 1,032 ns/op

И похоже, что победителями стали цикл while , Apache Commons и Guava!

8. Заключение

В этом руководстве мы рассмотрели несколько различных способов удаления пробельных символов в начале и в конце строки .

Мы использовали цикл while , String.replaceAll(), Pattern.matcher().replaceAll(), Apache Commons и Guava для получения этого результата.

Как всегда, код доступен на GitHub .