1. Обзор
В этом руководстве мы обсудим различные способы удаления стоп-слов из строки
в Java. Это полезная операция в тех случаях, когда мы хотим удалить нежелательные или запрещенные слова из текста, например комментарии или обзоры, добавленные пользователями онлайн-сайта.
Мы будем использовать простой цикл, Collection.removeAll()
и регулярные выражения.
Наконец, мы сравним их производительность с помощью Java Microbenchmark Harness .
2. Загрузка стоп-слов
Сначала мы загрузим стоп-слова из текстового файла.
Здесь у нас есть файл english_stopwords.txt
, который содержит список слов, которые мы считаем стоп-словами, таких как я
, он
, она
и .
Мы загрузим стоп-слова в список
строк , используя Files.readAllLines
()
:
@BeforeClass
public static void loadStopwords() throws IOException {
stopwords = Files.readAllLines(Paths.get("english_stopwords.txt"));
}
3. Удаление стоп-слов вручную
Для нашего первого решения мы удалим стоп-слова вручную, перебирая каждое слово и проверяя, является ли оно стоп-словом :
@Test
public void whenRemoveStopwordsManually_thenSuccess() {
String original = "The quick brown fox jumps over the lazy dog";
String target = "quick brown fox jumps lazy dog";
String[] allWords = original.toLowerCase().split(" ");
StringBuilder builder = new StringBuilder();
for(String word : allWords) {
if(!stopwords.contains(word)) {
builder.append(word);
builder.append(' ');
}
}
String result = builder.toString().trim();
assertEquals(result, target);
}
4. Использование Collection.removeAll()
Далее, вместо перебора каждого слова в нашей строке
, мы можем использовать Collection.removeAll()
для удаления всех стоп-слов сразу :
@Test
public void whenRemoveStopwordsUsingRemoveAll_thenSuccess() {
ArrayList<String> allWords =
Stream.of(original.toLowerCase().split(" "))
.collect(Collectors.toCollection(ArrayList<String>::new));
allWords.removeAll(stopwords);
String result = allWords.stream().collect(Collectors.joining(" "));
assertEquals(result, target);
}
В этом примере после разделения нашей строки
на массив слов мы преобразуем ее в ArrayList
, чтобы можно было применить метод removeAll()
.
5. Использование регулярных выражений
Наконец, мы можем создать регулярное выражение из нашего списка стоп-слов
, а затем использовать его для замены стоп-слов в нашей строке
:
@Test
public void whenRemoveStopwordsUsingRegex_thenSuccess() {
String stopwordsRegex = stopwords.stream()
.collect(Collectors.joining("|", "\\b(", ")\\b\\s?"));
String result = original.toLowerCase().replaceAll(stopwordsRegex, "");
assertEquals(result, target);
}
Результирующее выражение stopwordsRegex
будет иметь формат «\b(он|она|the|…)\b\s?». В этом регулярном выражении «\b» относится к границе слова, чтобы избежать замены, например, «he» в «heat», а «\s?» относится к нулю или одному пробелу, чтобы удалить лишний пробел после замены стоп-слова.
6. Сравнение производительности
Теперь давайте посмотрим, какой метод имеет лучшую производительность.
Во- первых, давайте настроим наш бенчмарк . Мы будем использовать довольно большой текстовый файл в качестве источника нашей строки
с именем Shakespeare-hamlet.txt
:
@Setup
public void setup() throws IOException {
data = new String(Files.readAllBytes(Paths.get("shakespeare-hamlet.txt")));
data = data.toLowerCase();
stopwords = Files.readAllLines(Paths.get("english_stopwords.txt"));
stopwordsRegex = stopwords.stream().collect(Collectors.joining("|", "\\b(", ")\\b\\s?"));
}
Затем у нас будут методы тестирования, начиная с removeManually()
:
@Benchmark
public String removeManually() {
String[] allWords = data.split(" ");
StringBuilder builder = new StringBuilder();
for(String word : allWords) {
if(!stopwords.contains(word)) {
builder.append(word);
builder.append(' ');
}
}
return builder.toString().trim();
}
Далее у нас есть тест removeAll()
:
@Benchmark
public String removeAll() {
ArrayList<String> allWords =
Stream.of(data.split(" "))
.collect(Collectors.toCollection(ArrayList<String>::new));
allWords.removeAll(stopwords);
return allWords.stream().collect(Collectors.joining(" "));
}
Наконец, мы добавим тест для replaceRegex()
:
@Benchmark
public String replaceRegex() {
return data.replaceAll(stopwordsRegex, "");
}
И вот результат нашего теста:
Benchmark Mode Cnt Score Error Units
removeAll avgt 60 7.782 ± 0.076 ms/op
removeManually avgt 60 8.186 ± 0.348 ms/op
replaceRegex avgt 60 42.035 ± 1.098 ms/op
Кажется, что использование Collection.removeAll()
имеет самое быстрое время выполнения, а использование регулярных выражений — самое медленное .
7. Заключение
В этой быстрой статье мы узнали о различных методах удаления стоп-слов из строки
в Java. Мы также сравнили их, чтобы увидеть, какой метод имеет наилучшую производительность.
Полный исходный код примеров доступен на GitHub .