让我们依次看一下每个方法对应的实际java实现(仅相关代码)。这本身就会回答他们的效率问题。
String.charAt:
public char charAt(int index) {
if ((index < 0) || (index >= value.length)) {
throw new StringIndexOutOfBoundsException(index);
}
return value[index];
}
正如我们所见,这只是一个恒定时间操作的单个数组访问。
StringBuffer.charAt:
public synchronized char charAt(int index) {
if ((index < 0) || (index >= count))
throw new StringIndexOutOfBoundsException(index);
return value[index];
}
同样,单数组访问,所以是恒定时间操作。
StringBuilder.charAt:
public char charAt(int index) {
if ((index < 0) || (index >= count))
throw new StringIndexOutOfBoundsException(index);
return value[index];
}
同样,单数组访问,所以是恒定时间操作。尽管这三种方法看起来都一样,但还是有一些细微的差别。例如,只有 StringBuffer.charAt 方法是同步的,其他方法是不同步的。类似地,if check 对于 String.charAt 略有不同(猜猜为什么?)。仔细观察这些方法实现本身,我们会发现它们之间的其他细微差别。
现在,让我们看看 deleteCharAt 的实现。
String 没有 deleteCharAt 方法。原因可能是它是一个不可变的对象。因此,公开一个明确表明此方法修改对象的 API 可能不是一个好主意。
StringBuffer 和 StringBuilder 都是 AbstractStringBuilder 的子类。这两个类的 deleteCharAt 方法将实现委托给其父类本身。
StringBuffer.deleteCharAt :
public synchronized StringBuffer deleteCharAt(int index) {
super.deleteCharAt(index);
return this;
}
StringBuilder.deleteCharAt :
public StringBuilder deleteCharAt(int index) {
super.deleteCharAt(index);
return this;
}
AbstractStringBuilder.deleteCharAt:
public AbstractStringBuilder deleteCharAt(int index) {
if ((index < 0) || (index >= count))
throw new StringIndexOutOfBoundsException(index);
System.arraycopy(value, index+1, value, index, count-index-1);
count--;
return this;
}
仔细观察 AbstractStringBuilder.deleteCharAt 方法会发现它实际上是在调用 System.arraycopy。在最坏的情况下,这可能是 O(N)。所以 deleteChatAt 方法的时间复杂度是 O(N)。