Более быстрые альтернативы, чтобы заменить метод в Строке Java? - PullRequest
14 голосов
/ 18 июня 2009

Тот факт, что метод replace возвращает строковый объект, а не заменяет содержимое заданной строки, немного туповат (но понятно, когда вы знаете, что строки неизменны в Java). Я беру основной удар по производительности, используя глубоко вложенную замену в некотором коде. Могу ли я заменить это чем-то, что сделало бы это быстрее?

Ответы [ 10 ]

22 голосов
/ 18 июня 2009

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

StringBuilder описывается так:

"Изменяемая последовательность символов. Этот класс предоставляет API, совместимый с StringBuffer, но без гарантии синхронизации".

Он имеет replaceappend, insert, delete и др.), И вы можете использовать toString, чтобы превратить его в настоящий String.

8 голосов
/ 18 июня 2009

Предыдущие сообщения верны, StringBuilder / StringBuffer - это решение.

Но вы также должны спросить себя, если это хорошая идея - заменить большие строки в памяти.

У меня часто бывают манипуляции со строками, которые реализованы в виде потока, поэтому вместо замены в строке и последующей отправки в OutputStream я выполняю замену в тот момент, когда отправляю строку в выходной поток. Это работает намного быстрее, чем любая замена.

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

6 голосов
/ 15 июля 2015

следующий код ок. В 30 раз быстрее, если нет совпадений, и в 5 раз быстрее, если есть совпадение.

static String fastReplace( String str, String target, String replacement ) {
    int targetLength = target.length();
    if( targetLength == 0 ) {
        return str;
    }
    int idx2 = str.indexOf( target );
    if( idx2 < 0 ) {
        return str;
    }
    StringBuilder buffer = new StringBuilder( targetLength > replacement.length() ? str.length() : str.length() * 2 );
    int idx1 = 0;
    do {
        buffer.append( str, idx1, idx2 );
        buffer.append( replacement );
        idx1 = idx2 + targetLength;
        idx2 = str.indexOf( target, idx1 );
    } while( idx2 > 0 );
    buffer.append( str, idx1, str.length() );
    return buffer.toString();
}
5 голосов
/ 18 июня 2009

Я согласен с вышеизложенным. Используйте StringBuffer для обеспечения безопасности потоков и StringBuilder при работе с отдельными потоками.

2 голосов
/ 19 июля 2014

В дополнение к ответу @paxdiablo приведен пример реализации replaceAll с использованием StringBuffers, который в ~ 3,7 раза быстрее, чем String.replaceAll ():

Код:

public static String replaceAll(final String str, final String searchChars, String replaceChars)
{
  if ("".equals(str) || "".equals(searchChars) || searchChars.equals(replaceChars))
  {
    return str;
  }
  if (replaceChars == null)
  {
    replaceChars = "";
  }
  final int strLength = str.length();
  final int searchCharsLength = searchChars.length();
  StringBuilder buf = new StringBuilder(str);
  boolean modified = false;
  for (int i = 0; i < strLength; i++)
  {
    int start = buf.indexOf(searchChars, i);

    if (start == -1)
    {
      if (i == 0)
      {
        return str;
      }
      return buf.toString();
    }
    buf = buf.replace(start, start + searchCharsLength, replaceChars);
    modified = true;

  }
  if (!modified)
  {
    return str;
  }
  else
  {
    return buf.toString();
  }
}

Контрольный пример - выходные данные следующие (Delta1 = 1917009502; Delta2 = 7241000026):

@Test
public void testReplaceAll() 
{
  String origStr = "1234567890-1234567890-";

  String replacement1 =  StringReplacer.replaceAll(origStr, "0", "a");
  String expectedRep1 = "123456789a-123456789a-";

  String replacement2 =  StringReplacer.replaceAll(origStr, "0", "ab");
  String expectedRep2 = "123456789ab-123456789ab-";

  String replacement3 =  StringReplacer.replaceAll(origStr, "0", "");
  String expectedRep3 = "123456789-123456789-";


  String replacement4 =  StringReplacer.replaceAll(origStr, "012", "a");
  String expectedRep4 = "1234567890-1234567890-";

  String replacement5 =  StringReplacer.replaceAll(origStr, "123", "ab");
  String expectedRep5 = "ab4567890-ab4567890-";

  String replacement6 =  StringReplacer.replaceAll(origStr, "123", "abc");
  String expectedRep6 = "abc4567890-abc4567890-";

  String replacement7 =  StringReplacer.replaceAll(origStr, "123", "abcdd");
  String expectedRep7 = "abcdd4567890-abcdd4567890-";

  String replacement8 =  StringReplacer.replaceAll(origStr, "123", "");
  String expectedRep8 = "4567890-4567890-";

  String replacement9 =  StringReplacer.replaceAll(origStr, "123", "");
  String expectedRep9 = "4567890-4567890-";

  assertEquals(replacement1, expectedRep1);
  assertEquals(replacement2, expectedRep2);
  assertEquals(replacement3, expectedRep3);
  assertEquals(replacement4, expectedRep4);
  assertEquals(replacement5, expectedRep5);
  assertEquals(replacement6, expectedRep6);
  assertEquals(replacement7, expectedRep7);
  assertEquals(replacement8, expectedRep8);
  assertEquals(replacement9, expectedRep9);

  long start1 = System.nanoTime();
  for (long i = 0; i < 10000000L; i++)
  {
    String rep =  StringReplacer.replaceAll(origStr, "123", "abcdd");
  }
  long delta1 = System.nanoTime() -start1;

  long start2= System.nanoTime();

  for (long i = 0; i < 10000000L; i++)
  {
    String rep =  origStr.replaceAll( "123", "abcdd");
  }

  long delta2 = System.nanoTime() -start1;

  assertTrue(delta1 < delta2);

  System.out.printf("Delta1 = %d; Delta2 =%d", delta1, delta2);


}
1 голос
/ 15 сентября 2011

Просто возьмите char[] из String и повторите его. Используйте временную StringBuilder.

Ищите шаблон, который вы хотите заменить во время итерации, если вы не нашли шаблон, запишите отсканированный материал в StringBuilder, иначе запишите текст замены в StringBuilder.

1 голос
/ 07 сентября 2011

Если вам нужно заменить несколько строк (например, escape-последовательности XML), особенно если замены отличаются по длине от шаблона, алгоритм типа лексера FSM кажется наиболее эффективным, аналогично предложению обработки потоковая мода, где вывод строится постепенно.

Возможно, объект Matcher мог бы быть использован для эффективной работы.

0 голосов
/ 22 апреля 2019

Поскольку String.replace(CharSequence target, CharSequence replacement) имеет Pattern.compile, matcher, replaceAll внутри, его можно слегка оптимизировать для использования предварительно скомпилированной целевой константы шаблона, например:

private static final Pattern COMMA_REGEX = Pattern.compile(",");
...
COMMA_REGEX.matcher(value).replaceAll(replacement);
0 голосов
/ 19 мая 2015

Когда вы заменяете отдельные символы, рассмотрите возможность перебора массива символов, но замените символы с помощью (предварительно созданного) HashMap<Character, Character>().

Я использую эту стратегию для преобразования строки целого показателя в символы верхнего индекса в юникоде.

Это примерно вдвое быстрее, чем String.replace(char, char). Обратите внимание, что время, связанное с созданием хэш-карты, не включено в это сравнение.

0 голосов
/ 18 июня 2009

Все манипуляции со строками в целом очень медленные. Рассмотрите возможность использования StringBuffer, он не совсем похож на класс String, но у него много общего и он также изменчив.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...