首先应该提到的是,StringBuilder 通常比StringBuffer 更受欢迎。来自StringBuffer's own API:
从 JDK 5 版本开始,该类已经补充了一个为单线程使用而设计的等效类 StringBuilder。 StringBuilder 类通常应该优先于这个类,因为它支持所有相同的操作,但速度更快,因为它不执行同步。
也就是说,我会坚持StringBuffer 来回答其余的问题,因为这就是你要问的; StringBuffer 所做的一切,StringBuilder 也...除了同步,这通常是不需要的。因此,除非您在多个线程中使用缓冲区,否则切换到 StringBuilder 是一项简单的任务。
问题
StringBuffer sb = new StringBuffer("teststr");
“现在我必须将sb 的值更改为"testString" 而不清空内容”
所以您希望sb 在其缓冲区中有String 值"testString"?有很多方法可以做到这一点,我将列出其中的一些来说明如何使用 API。
最佳解决方案:它执行从"teststr" 到"testString" 的最小编辑。不可能比这更快。
StringBuffer sb = new StringBuffer("teststr");
sb.setCharAt(4, 'S');
sb.append("ing");
assert sb.toString().equals("testString");
这会不必要地用"tr" 覆盖"tr"。
StringBuffer sb = new StringBuffer("teststr");
sb.replace(4, sb.length(), "String");
assert sb.toString().equals("testString");
这涉及由于deleteCharAt 和insert 而导致的班次。
StringBuffer sb = new StringBuffer("teststr");
sb.deleteCharAt(4);
sb.insert(4, 'S');
sb.append("ing");
assert sb.toString().equals("testString");
现在有点不同:它不会神奇地知道它有 "teststr" 需要编辑为 "testString";它仅假设StringBuffer 在某处至少包含一次"str",并且需要将其替换为"String"。
StringBuffer sb = new StringBuffer("strtest");
int idx = sb.indexOf("str");
sb.replace(idx, idx + 3, "String");
assert sb.toString().equals("Stringtest");
现在假设您要替换 ALL 次出现的 "str" 并将其替换为 "String"。 StringBuffer 没有内置此功能。您可以尝试以最有效的方式自己完成,无论是就地(可能使用 2-pass 算法)或使用第二个 StringBuffer 等。
但我将使用来自String 的replace(CharSequence, CharSequence)。在大多数情况下,这已经绰绰有余,而且绝对更清晰,更容易维护。它在输入字符串的长度上是线性的,所以它是渐近最优的。
String before = "str1str2str3";
String after = before.replace("str", "String");
assert after.equals("String1String2String3");
讨论
“我正在寻找稍后使用以前的内存位置分配值的方法”
确切的内存位置不应该是您真正关心的问题;事实上,StringBuilder 和StringBuffer 都会在必要时将其内部缓冲区重新分配到不同的内存位置。防止这种情况发生的唯一方法是ensureCapacity(或通过构造函数设置),以便其内部缓冲区始终足够大,并且永远不需要重新分配。
但是,即使StringBuffer 确实偶尔重新分配其内部缓冲区,在大多数情况下也不应该成为问题。大多数动态增长的数据结构(ArrayList、HashMap 等)都以保留算法最优操作的方式执行它们,并利用成本摊销。我不会在这里进行摊销分析,但除非您正在做实时系统等,否则对于大多数应用程序来说这应该不是问题。
显然,我不了解您的具体需求,但担心过早优化,因为您似乎在担心大多数人不必担心的事情。