Подтвердить что ты не робот

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, и иметь много длинных линий, таких как один выше?"

4b9b3361

Ответ 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 итерациями, вы увидите значительное различие в производительности от "бесполезных" оптимизаций.

в большинстве случаев, однако, лучше всего ошибиться на стороне читаемости