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 .