Я хотел бы заменить этот код на более эффективный:
String s = "The brown fox is jumping over a fence";
s = s.replace(s, "brown", "black");
s = s.replace(s, "fox", "dog");
s = s.replace(s, "fence", "pond");
System.out.println(s);
Приведенный выше код выполняется через длинный текст 3 раза и каждый раз создает новые строки. Есть ли какая-нибудь библиотека, которая может выполнить ту же работу всего за одну итерацию по длинному тексту.
Я бы использовал StringBuilder, просмотрел текст по символам и попытался найти одну из поисковых фраз, а затем попытался эффективно создать новый текст.
Но я не хочу реализовывать сам, ищу для этого уже существующий библиотечный метод. (Apache, Google, ....)
Спасибо.
@ g00se Ваше решение также может иметь проблемы, если какой-либо токен окажется подстрокой более крупного слова, например. fenced
будет заменен вашим однострочником.
@TimBiegeleisen Нет, потому что токеном будет fenced
, который не является ключом в Map
, ключи которого не работают через подстроку
Если вы действительно хотите сделать только один проход по строке, вы можете выполнить поиск по регулярному выражению по чередованию, а затем выборочно выполнить замены на основе каждого конкретного совпадения. Например:
String s = "The brown fox is jumping over a fence";
String pattern = "\\b(?:brown|fox|fence)\\b";
Pattern r = Pattern.compile(pattern);
Matcher m = r.matcher(s);
StringBuffer buffer = new StringBuffer();
while (m.find()) {
switch (m.group()) {
case "brown":
m.appendReplacement(buffer, "black");
break;
case "fox":
m.appendReplacement(buffer, "dog");
break;
case "fence":
m.appendReplacement(buffer, "pond");
break;
}
}
m.appendTail(buffer);
System.out.println(buffer.toString()); // The black dog is jumping over a pond
Приведенный выше код очень многословен, но если вы выполняли эту замену для очень большого текста (например, от 100+ МБ до ГБ), тогда можно было бы повысить производительность, просто выполняя один проход по тексту, а не отдельные проходы. для каждого поискового запроса.
Вы можете попробовать StringUtils от Apache.
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
StringUtils.replaceEachRepeatedly();
StringUtils.replaceEach()
/**
* <p>
* Replaces all occurrences of Strings within another String.
* </p>
*
* <p>
* A {@code null} reference passed to this method is a no-op, or if
* any "search string" or "string to replace" is null, that replace will be
* ignored.
* </p>
*
* <pre>
* StringUtils.replaceEachRepeatedly(null, *, *) = null
* StringUtils.replaceEachRepeatedly("", *, *) = ""
* StringUtils.replaceEachRepeatedly("aba", null, null) = "aba"
* StringUtils.replaceEachRepeatedly("aba", new String[0], null) = "aba"
* StringUtils.replaceEachRepeatedly("aba", null, new String[0]) = "aba"
* StringUtils.replaceEachRepeatedly("aba", new String[]{"a"}, null) = "aba"
* StringUtils.replaceEachRepeatedly("aba", new String[]{"a"}, new String[]{""}) = "b"
* StringUtils.replaceEachRepeatedly("aba", new String[]{null}, new String[]{"a"}) = "aba"
* StringUtils.replaceEachRepeatedly("abcde", new String[]{"ab", "d"}, new String[]{"w", "t"}) = "wcte"
* (example of how it repeats)
* StringUtils.replaceEachRepeatedly("abcde", new String[]{"ab", "d"}, new String[]{"d", "t"}) = "tcte"
* StringUtils.replaceEachRepeatedly("abcde", new String[]{"ab", "d"}, new String[]{"d", "ab"}) = IllegalStateException
* </pre>
*
* @param text
* text to search and replace in, no-op if null
* @param searchList
* the Strings to search for, no-op if null
* @param replacementList
* the Strings to replace them with, no-op if null
* @return the text with any replacements processed, {@code null} if
* null String input
* @throws IllegalStateException
* if the search is repeating and there is an endless loop due
* to outputs of one being inputs to another
* @throws IllegalArgumentException
* if the lengths of the arrays are not the same (null is ok,
* and/or size 0)
* @since 2.4
*/
public static String replaceEachRepeatedly(final String text, final String[] searchList, final String[] replacementList) {
// timeToLive should be 0 if not used or nothing to replace, else it's
// the length of the replace array
final int timeToLive = searchList == null ? 0 : searchList.length;
return replaceEach(text, searchList, replacementList, true, timeToLive);
}
/**
* <p>
* Replaces all occurrences of Strings within another String.
* </p>
*
* <p>
* A {@code null} reference passed to this method is a no-op, or if
* any "search string" or "string to replace" is null, that replace will be
* ignored. This will not repeat. For repeating replaces, call the
* overloaded method.
* </p>
*
* <pre>
* StringUtils.replaceEach(null, *, *) = null
* StringUtils.replaceEach("", *, *) = ""
* StringUtils.replaceEach("aba", null, null) = "aba"
* StringUtils.replaceEach("aba", new String[0], null) = "aba"
* StringUtils.replaceEach("aba", null, new String[0]) = "aba"
* StringUtils.replaceEach("aba", new String[]{"a"}, null) = "aba"
* StringUtils.replaceEach("aba", new String[]{"a"}, new String[]{""}) = "b"
* StringUtils.replaceEach("aba", new String[]{null}, new String[]{"a"}) = "aba"
* StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"w", "t"}) = "wcte"
* (example of how it does not repeat)
* StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"d", "t"}) = "dcte"
* </pre>
*
* @param text
* text to search and replace in, no-op if null
* @param searchList
* the Strings to search for, no-op if null
* @param replacementList
* the Strings to replace them with, no-op if null
* @return the text with any replacements processed, {@code null} if
* null String input
* @throws IllegalArgumentException
* if the lengths of the arrays are not the same (null is ok,
* and/or size 0)
* @since 2.4
*/
public static String replaceEach(final String text, final String[] searchList, final String[] replacementList) {
return replaceEach(text, searchList, replacementList, false, 0);
}
Это также делает свою работу. Почему вы минусуете? Когда вы просматриваете код StringUtils, он использует тот же поиск по регулярным выражениям и соединение StringBuilder, что и другое решение, упомянутое здесь.
Map<String, String> m = Map.of("brown", "black", "fox", "dog", "fence", "pond");String s2 = new Scanner(s).tokens().map(t ->m.getOrDefault(t, t)).collect(Collectors.joining(" "));
было бы довольно эффективно