Java CharAt() 和 deleteCharAt() 性能
我一直想知道java中String/StringBuilder/StringBuffer
的charAt
函数的实现 那有多复杂? 还有 StringBuffer/StringBuilder
中的 deleteCharAt()
怎么样?
I've been wondering about the implementation of charAt
function for String/StringBuilder/StringBuffer
in java
what is the complexity of that ?
also what about the deleteCharAt()
in StringBuffer/StringBuilder
?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
对于
String
、StringBuffer
和StringBuilder
,charAt()
是常量时间操作。对于
StringBuffer
和StringBuilder
,deleteCharAt()
是线性时间操作。StringBuffer
和StringBuilder
具有非常相似的性能特征。主要区别在于前者是同步的(因此是线程安全的),而后者不是。For
String
,StringBuffer
, andStringBuilder
,charAt()
is a constant-time operation.For
StringBuffer
andStringBuilder
,deleteCharAt()
is a linear-time operation.StringBuffer
andStringBuilder
have very similar performance characteristics. The primary difference is that the former issynchronized
(so is thread-safe) while the latter is not.让我们依次看看每个方法对应的实际java实现(仅相关代码)。这本身就会回答他们的效率。
String.charAt :
正如我们所看到的,这只是一个恒定时间操作的单个数组访问。
StringBuffer.charAt :
同样,单个数组访问,因此是恒定时间操作。
StringBuilder.charAt :
同样,单个数组访问,因此是恒定时间操作。尽管这三种方法看起来相同,但还是有一些细微的差别。例如,只有 StringBuffer.charAt 方法被同步,而其他方法则不被同步。同样,String.charAt 的 if check 略有不同(猜猜为什么?)。仔细观察这些方法实现本身,我们会发现它们之间还存在其他细微差别。
现在,让我们看看 deleteCharAt 的实现。
String没有deleteCharAt方法。原因可能是它是一个不可变的对象。因此,公开一个明确指示此方法修改对象的 API 可能不是一个好主意。
StringBuffer 和 StringBuilder 都是 AbstractStringBuilder 的子类。这两个类的deleteCharAt方法将实现委托给其父类本身。
StringBuffer.deleteCharAt :
StringBuilder.deleteCharAt :
AbstractStringBuilder.deleteCharAt :
仔细观察 AbstractStringBuilder.deleteCharAt 方法会发现它实际上是在调用 System.arraycopy。在最坏的情况下,这可能是 O(N)。所以deleteChatAt方法的时间复杂度是O(N)。
Let us just look at the corresponding actual java implementation(only relevant code) for each of these methods in turn. That itself will answer about their efficiency.
String.charAt :
As we can see, it is just a single array access which is a constant time operation.
StringBuffer.charAt :
Again, single array access, so a constant time operation.
StringBuilder.charAt :
Again, single array access, so a constant time operation. Even though all these three methods look same, there are some minor differences. For example, only StringBuffer.charAt method is synchronized but not other methods. Similarly if check is slightly different for String.charAt (guess why?). Closer look at these method implementations itself give us other minor differences among them.
Now, let us look at deleteCharAt implementations.
String does not have deleteCharAt method. The reason might be it is an immutable object. So exposing an API which explicitly indicates that this method modifies the object is not probably a good idea.
Both StringBuffer and StringBuilder are subclasses of AbstractStringBuilder. The deleteCharAt method of these two classes is delegating the implementation to its parent class itself.
StringBuffer.deleteCharAt :
StringBuilder.deleteCharAt :
AbstractStringBuilder.deleteCharAt :
A closer look at AbstractStringBuilder.deleteCharAt method reveals that it is actually calling System.arraycopy. This can be O(N) in worst case. So deleteChatAt method is O(N) time complexity.
charAt
方法的复杂度为O(1)
。假设您要删除随机字符,
StringBuilder
和StringBuffer
上的deleteCharAt
方法平均时间为O(N)
来自N
字符StringBuffer
/StringBuilder
。 (平均而言,它必须移动一半的剩余字符来填充已删除字符留下的“洞”。没有多次操作的摊销;请参见下文。)但是,如果您删除最后一个字符,成本将为O(1)
。String
没有deleteCharAt
方法。理论上,
StringBuilder
和StringBuffer
可以针对通过缓冲区“传递”插入或删除多个字符的情况进行优化。他们可以通过在缓冲区中维护一个可选的“间隙”并在其中移动字符来实现这一点。 (IIRC,emacs 以这种方式实现其文本缓冲区。)这种方法的问题是:charAt
在获取字符数组元素之前,必须将offset
与间隙的起点和终点进行比较,并对实际索引值进行相应的调整。毫不奇怪,这种“优化”尚未在标准
StringBuilder
/StringBuffer
类中实现。但是,自定义CharSequence
类可以使用此方法。The
charAt
method isO(1)
.The
deleteCharAt
method onStringBuilder
andStringBuffer
isO(N)
on average, assuming you are deleting a random character from anN
characterStringBuffer
/StringBuilder
. (It has to move, on average, half of the remaining characters to fill up the "hole" left by the deleted character. There is no amortization over multiple operations; see below.) However, if you delete the last character, the cost will beO(1)
.There is no
deleteCharAt
method forString
.In theory,
StringBuilder
andStringBuffer
could be optimized for the case where you are inserting or deleting multiple characters in a "pass" through the buffer. They could do this by maintaining an optional "gap" in the buffer, and moving characters across it. (IIRC, emacs implements its text buffers this way.) The problems with this approach are:charAt
would have to compare theoffset
with the start and end points of the gap, and make the corresponding adjustments to the actual index value before fetching the character array element.Not surprisingly, this "optimization" has not been implemented in the standard
StringBuilder
/StringBuffer
classes. However, a customCharSequence
class could use this approach.charAt
速度非常快(并且可以使用 String 的内部函数),它是数组的简单索引。deleteCharAt
需要一个数组副本,因此删除一个字符不会很快。charAt
is super fast (and can use intrinsics for String), it's a simple index into an array.deleteCharAt
would require an arraycopy, thus deleting a char won't be fast.因为我们都知道字符串在JDK中是作为字符数组来实现的,它实现了
randomAccess
接口。因此charAt
的时间复杂度应该是int O(1)。与其他数组一样,删除操作的时间复杂度为 O(n)。Since we all know that the string is implemented in JDK as a character array, which implements the
randomAccess
interface. Therefore the time complexity ofcharAt
should be int O(1). As other arrays, the delete operation has theO(n)
time complexity.以上所有回复的摘要:
charAt 的复杂度为 O(1),因为它只是访问数组的索引
在最坏的情况下,deleteCharAt 可能是 O(N),因为它会为其复制整个数组。
Summary of all responses from above:
charAt is O(1) since its just accessing the index of an array
deleteCharAt can be O(N) in worst case since it copies the entire array for it.