StringBuilder против String с заменой
При выполнении конкатенирования множества строк мне было рекомендовано сделать это с помощью StringBuilder
как такового:
StringBuilder someString = new StringBuilder("abc");
someString.append("def");
someString.append("123");
someString.append("moreStuff");
в отличие от
String someString = "abc";
someString = someString + "def";
someString = someString + "123";
someString = someString + "moreStuff";
что приведет к созданию довольно многих строк, в отличие от одного.
Теперь мне нужно сделать аналогичную вещь, но вместо использования конкатенации я использую метод replace
для String как таковой:
String someString = SOME_LARGE_STRING_CONSTANT;
someString = someString.replace("$VARIABLE1", "abc");
someString = someString.replace("$VARIABLE2", "def");
someString = someString.replace("$VARIABLE3", "123");
someString = someString.replace("$VARIABLE4", "moreStuff");
Чтобы выполнить то же самое с помощью StringBuilder, я должен сделать это, просто для замены:
someString.replace(someString.indexOf("$VARIABLE1"), someString.indexOf("$VARIABLE1")+10, "abc");
Итак, мой вопрос: "Лучше ли использовать String.replace и создать много дополнительных строк, или еще использовать StringBuilder, и иметь много длинных линий, таких как один выше?"
Ответы
Ответ 1
Верно, что StringBuilder имеет тенденцию быть лучше, чем конкатенация или изменение строк вручную, поскольку StringBuilder изменен, а String неизменен, и вам нужно создать новую строку для каждой модификации.
Просто отметим, что Java-компилятор автоматически преобразует пример следующим образом:
String result = someString + someOtherString + anotherString;
во что-то вроде:
String result = new StringBuilder().append(someString).append(someOtherString).append(anotherString).toString();
Тем не менее, если вы не заменяете множество строк, перейдите в зависимости от того, что более читаемо и доступно для обслуживания. Итак, если вы можете сохранить его более чистым, получив последовательность вызовов "replace", продолжайте и делайте это над методом StringBuilder. Разница будет пренебрежимо мала по сравнению с стрессом, который вы сохраняете, от печальной трагедии микрооптимизации.
PS
Для вашего образца кода (который, как указал OscarRyz, не будет работать, если у вас есть более одного "$VARIABLE1"
в someString
, и в этом случае вам нужно будет использовать цикл), вы можете кэшировать результат вызова indexOf
:
someString.replace(someString.indexOf("$VARIABLE1"), someString.indexOf("$VARIABLE1")+10, "abc");
С
int index = someString.indexOf("$VARIABLE1");
someString.replace(index, index+10, "abc");
Не нужно искать строку дважды: -)
Ответ 2
Угадайте, что? Если вы работаете с Java 1.5+, конкатенация работает одинаково со строковыми литералами
String h = "hello" + "world";
и
String i = new StringBuilder().append("hello").append("world").toString();
То же самое.
Итак, компилятор уже сделал эту работу для вас.
Конечно, лучше было бы:
String j = "hellworld"; // ;)
Что касается второго, то это предпочтительнее, но должно быть так сложно, с силой "поиска и замены" и немного регулярного выражения foo
Например, вы можете определить метод, подобный методу в этом примере:
public static void replace( String target, String replacement,
StringBuilder builder ) {
int indexOfTarget = -1;
while( ( indexOfTarget = builder.indexOf( target ) ) >= 0 ) {
builder.replace( indexOfTarget, indexOfTarget + target.length() , replacement );
}
}
И теперь ваш код выглядит следующим образом:
someString = someString.replace("VARIABLE1", "abc");
someString = someString.replace("VARIABLE2", "xyz");
Все, что вам нужно сделать, это захватить текстовый редактор триггером, похожим на этот поиск vi и заменить:
%s/^.*("\(.*\)".\s"\(.*\)");/replace("\1","\2",builder);
Это гласит: "возьмите что-нибудь в скобках и получится как строковый литерал и поместите его в эту другую строку".
И ваш код будет выглядеть следующим образом:
someString = someString.replace("VARIABLE1", "abc");
someString = someString.replace("VARIABLE2", "xyz");
:
replace( "VARIABLE1", "abc", builder );
replace( "VARIABLE2", "xyz", builder );
В мгновение ока.
Вот рабочая демонстрация:
class DoReplace {
public static void main( String ... args ) {
StringBuilder builder = new StringBuilder(
"LONG CONSTANT WITH VARIABLE1 and VARIABLE2 and VARIABLE1 and VARIABLE2");
replace( "VARIABLE1", "abc", builder );
replace( "VARIABLE2", "xyz", builder );
System.out.println( builder.toString() );
}
public static void replace( String target, String replacement,
StringBuilder builder ) {
int indexOfTarget = -1;
while( ( indexOfTarget = builder.indexOf( target ) ) > 0 ) {
builder.replace( indexOfTarget, indexOfTarget + target.length() ,
replacement );
}
}
}
Ответ 3
Я бы сказал, что нужно использовать StringBuilder, но просто напишите обертку, которая облегчает чтение кода и, следовательно, более удобную для пользователя, при этом сохраняя эффективность. = D
import java.lang.StringBuilder;
public class MyStringBuilder
{
StringBuilder sb;
public MyStringBuilder()
{
sb = new StringBuilder();
}
public void replace(String oldStr, String newStr)
{
int start = -1;
while ((start = sb.indexOf(oldStr)) > -1)
{
int end = start + oldStr.length();
sb.replace(start, end, newStr);
}
}
public void append(String str)
{
sb.append(str);
}
public String toString()
{
return sb.toString();
}
//.... other exposed methods
public static void main(String[] args)
{
MyStringBuilder sb = new MyStringBuilder();
sb.append("old old olD dudely dowrite == pwn");
sb.replace("old", "new");
System.out.println(sb);
}
}
ВЫВОД:
new new olD dudely dowrite == pwn
Теперь вы можете просто использовать новую версию, которая является одним легким лайнером
MyStringBuilder mySB = new MyStringBuilder();
mySB.append("old dudley dowrite == pwn");
mySB.replace("old", "new"):
Ответ 4
Вместо таких длинных строк вы можете просто написать метод для замены частей строк StringBuilder, что-то вроде строк:
public StringBuilder replace(StringBuilder someString, String replaceWhat, String replaceWith) {
return someString.replace(someString.indexOf(replaceWhat), someString.indexOf(replaceWhat)+replaceWhat.length(), replaceWith);
}
Ответ 5
Может быть, класс String внутренне использует
IndexOf
чтобы найти индекс старой строки и заменить ее на новую строку.
А также StringBuilder не является потокобезопасным, поэтому он выполняется намного быстрее.
Ответ 6
Если ваша строка действительно большая, и вы беспокоитесь об эффективности, я бы рекомендовал написать класс, который принимает текст вашего шаблона и список переменных, а затем читает символ строки источника по символу и строит результат с помощью StringBuilder. Это должно быть наиболее эффективным как с точки зрения использования ЦП, так и использования памяти. Кроме того, если вы читаете этот шаблонный текст из файла, я бы не загрузил его все в память спереди. Обработайте его в кусках, когда вы читаете его из файла.
Если вы просто ищете хороший способ построить строку, которая не так эффективна, как StringBuilder, но более эффективна, чем добавление строк снова и снова, вы можете использовать string.Format(). Он работает как sprintf() в C. MessageFormat.format() является опцией, но он использует StringBuffer.
Здесь есть еще один смежный вопрос: Вставка строки Java в другую строку без конкатенации?
Ответ 7
Коды всех парней имеют ошибку .try yourReplace("x","xy")
. Это будет цикл бесконечно
Ответ 8
Jam Hong правильная - все вышеперечисленные решения содержат потенциал для бесконечной петли. Думаю, урок, который нужно убрать, заключается в том, что микро оптимизация может часто вызывать всевозможные ужасные проблемы и на самом деле не спасает вас. Тем не менее, как бы то ни было - вот решение, которое не будет бесконечным циклом.
private static void replaceAll(StringBuilder builder, String replaceWhat, String replaceWith){
int occuranceIndex = builder.indexOf(replaceWhat);
int lastReplace = -1;
while(occuranceIndex >= 0){
if(occuranceIndex >= lastReplace){
builder.replace(occuranceIndex, occuranceIndex+replaceWhat.length(), replaceWith);
lastReplace = occuranceIndex + replaceWith.length();
occuranceIndex = builder.indexOf(replaceWhat);
}else{
break;
}
}
}
Ответ 9
в то время как верно, что микрооптимизация может быть проблематичной, иногда это зависит от контекста, например, если ваша замена выполняется для запуска внутри цикла с 10000 итерациями, вы увидите значительное различие в производительности от "бесполезных" оптимизаций.
в большинстве случаев, однако, лучше всего ошибиться на стороне читаемости