是否可以更快地替换Java String中的方法?

问题描述 投票:14回答:10

replace方法返回一个字符串对象而不是替换给定字符串的内容这一事实有点迟钝(但是当你知道字符串在Java中是不可变的时,这是可以理解的)。通过在某些代码中使用深层嵌套替换,我获得了重大的性能提升。有什么我可以用它取代它会使它更快吗?

java replace
10个回答
21
投票

这就是StringBuilder的意思。如果您要进行大量操作,请在StringBuilder上进行操作,然后在需要时将其转换为String

因此描述了StringBuilder

“一个可变的字符序列。这个类提供了与StringBuffer兼容的API,但不保证同步”。

它有replace(和appendinsertdelete等),你可以使用toString将其变成真正的String


0
投票

因为String.replace(CharSequence target, CharSequence replacement)里面有Pattern.compilematcherreplaceAll,所以可以通过使用预编译的目标模式常量来稍微优化它,如下所示:

private static final Pattern COMMA_REGEX = Pattern.compile(",");
...
COMMA_REGEX.matcher(value).replaceAll(replacement);

8
投票

以前的帖子是对的,StringBuilder / StringBuffer是一个解决方案。

但是,您还必须质疑在内存中对大字符串进行替换是否是个好主意。

我经常将String操作实现为流,因此我不是在字符串中替换它然后将其发送到OutputStream,而是在我将String发送到输出流时进行替换。这比任何替换都快得多。

如果您希望此替换实现模板机制,则此工作速度要快得多。流式传输总是更快,因为您消耗更少的内存,如果客户端速度慢,您只需要以慢速生成 - 因此它可以更好地扩展。


6
投票

以下代码是约。如果没有匹配则快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
投票

我同意以上所述。使用单线程时,使用StringBuffer进行线程安全和StringBuilder


2
投票

添加到@paxdiablo答案,这里是使用StringBuffers的replaceAll的示例实现,比String.replaceAll()快〜3.7倍:

码:

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
投票

如果要替换多个字符串(例如XML转义序列),特别是在替换与模式长度不同的情况下,FSM词法分析器类型算法似乎最有效,类似于以流方式处理的建议,输出逐步建立。

也许Matcher对象可以用来有效地做到这一点。


1
投票

只需获取char[]String并迭代它。使用临时StringBuilder

如果找不到模式,请在迭代时查找要替换的模式,将扫描的内容写入StringBuilder,否则将替换文本写入StringBuilder


0
投票

一般来说,所有字符串操作都非常慢。考虑使用StringBuffer,它与String类不完全相同,但有许多共同之处,它也是可变的。


0
投票

当您替换单个字符时,请考虑迭代字符数组,但使用(预先创建的)HashMap<Character, Character>()替换字符。

我使用此策略通过unicode上标字符转换整数指数字符串。

它的速度是String.replace(char, char)的两倍。请注意,此比较中不包括与创建哈希映射关联的时间。

© www.soinside.com 2019 - 2024. All rights reserved.