То 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 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");

поэтому мой вопрос: "лучше ли использовать строку.заменить и создать много дополнительных строк или использовать StringBuilder и иметь много длинных строк, таких как выше?"

9 ответов


верно, что StringBuilder имеет тенденцию быть лучше, чем конкатенация или изменение строк вручную, поскольку StringBuilder является изменяемым, в то время как String является неизменяемым, и вам нужно создать новую строку для каждой модификации.

просто обратите внимание, что компилятор Java автоматически преобразует такой пример:

String result = someString + someOtherString + anotherString;

в что-то вроде:

String result = new StringBuilder().append(someString).append(someOtherString).append(anotherString).toString();

это сказало, если вы не заменяете много строк,пойти на какой более читаемый и более ремонтопригодный. поэтому, если вы можете сохранить его чище, имея последовательность вызовов "replace", продолжайте и делайте это по методу StringBuilder. Разница будет незначительной по сравнению со стрессом, который вы спасаете от борьбы с печальная трагедия микро-оптимизаций.

PS

для вашего образца кода (который, как отметил Оскаррис, не будет работать, если у вас есть более одного "$VARIABLE1" на someString, в этом случае вам нужно будет использовать loop), вы можете кэшировать результат indexOf звонок в:

someString.replace(someString.indexOf("$VARIABLE1"), someString.indexOf("$VARIABLE1")+10, "abc");

С

int index = someString.indexOf("$VARIABLE1");    
someString.replace(index, index+10, "abc");

нет необходимости искать строку дважды : -)


угадайте, что? Если вы работаете с Java 1.5+, конкатенация работает одинаково со строковыми литералами

  String h = "hello" + "world";

и

  String i = new StringBuilder().append("hello").append("world").toString();

то же самое.

Итак, компилятор уже сделал работу за вас.

конечно лучше было бы:

 String j = "hellworld"; // ;) 

Что касается второго, да, это предпочтительнее, но не должно быть так сложно, с силой "поиска и замены" и немного regex 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("","",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 );
    }
  }
}

Я бы сказал, пойти на использование 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"):

вместо таких длинных строк вы можете просто написать метод для замены частей строк StringBuilder, что-то вроде этого:

public StringBuilder replace(StringBuilder someString, String replaceWhat, String replaceWith) {
   return someString.replace(someString.indexOf(replaceWhat), someString.indexOf(replaceWhat)+replaceWhat.length(), replaceWith);
}

может быть класс String внутренне использует

indexOf

метод для поиска индекса старой строки и замены его новой строкой.

а также StringBuilder не является threadsafe, поэтому он выполняется намного быстрее.


Если ваша строка действительно большая, и вы беспокоитесь о производительности, я бы рекомендовал написать класс, который принимает текст шаблона и список переменных, а затем читает исходный символ строки по символу и строит результат с помощью StringBuilder. Это должно быть наиболее эффективным как с точки зрения процессора и памяти. Кроме того, если Вы читаете этот текст шаблона из файла, я бы не загружал его все в память спереди. Обработайте его в кусках, как Вы читаете его из файл.

Если вы просто ищете хороший способ построить строку, которая не так эффективна, как StringBuilder, но более эффективна, чем добавление строк снова и снова, вы можете использовать строку.format (). Он работает как sprintf() в с. messageformat значение.format () тоже вариант, но он использует StringBuffer.

здесь есть еще один связанный с этим вопрос:вставка строки Java в другую строку без конкатенации?


коды всех парней имеют ошибку .попробуй yourReplace("x","xy").Это будет цикл бесконечно


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;
        }
    }
}

хотя это правда, что микро-оптимизация может быть проблематичной, иногда это зависит от контекста, например, если ваша замена происходит внутри цикла с 10000 итерациями, вы увидите значительную разницу в производительности от "бесполезных" оптимизаций.

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