Java CharAt() 和 deleteCharAt() 性能

发布于 2024-11-17 07:26:49 字数 175 浏览 2 评论 0原文

我一直想知道java中String/StringBuilder/StringBuffercharAt函数的实现 那有多复杂? 还有 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

凝望流年 2024-11-24 07:26:49

对于 StringStringBufferStringBuildercharAt() 是常量时间操作。

对于 StringBufferStringBuilderdeleteCharAt() 是线性时间操作。

StringBufferStringBuilder 具有非常相似的性能特征。主要区别在于前者是同步的(因此是线程安全的),而后者不是。

For String, StringBuffer, and StringBuilder, charAt() is a constant-time operation.

For StringBuffer and StringBuilder, deleteCharAt() is a linear-time operation.

StringBuffer and StringBuilder have very similar performance characteristics. The primary difference is that the former is synchronized (so is thread-safe) while the latter is not.

木槿暧夏七纪年 2024-11-24 07:26:49

让我们依次看看每个方法对应的实际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 方法被同步,而其他方法则不被同步。同样,String.charAt 的 if check 略有不同(猜猜为什么?)。仔细观察这些方法实现本身,我们会发现它们之间还存在其他细微差别。

现在,让我们看看 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)。

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 :

public char charAt(int index) {
    if ((index < 0) || (index >= value.length)) {
        throw new StringIndexOutOfBoundsException(index);
    }
    return value[index];
}

As we can see, it is just a single array access which is a constant time operation.

StringBuffer.charAt :

public synchronized char charAt(int index) {
  if ((index < 0) || (index >= count))
    throw new StringIndexOutOfBoundsException(index);
  return value[index];
}

Again, single array access, so a constant time operation.

StringBuilder.charAt :

public char charAt(int index) {
    if ((index < 0) || (index >= count))
        throw new StringIndexOutOfBoundsException(index);
    return value[index];
}

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 :

  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;
    }

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.

眼泪也成诗 2024-11-24 07:26:49

charAt 方法的复杂度为O(1)

假设您要删除随机字符,StringBuilderStringBuffer 上的 deleteCharAt 方法平均时间为 O(N)来自 N 字符 StringBuffer / StringBuilder。 (平均而言,它必须移动一半的剩余字符来填充已删除字符留下的“洞”。没有多次操作的摊销;请参见下文。)但是,如果您删除最后一个字符,成本将为O(1)

String 没有 deleteCharAt 方法。


理论上,StringBuilderStringBuffer 可以针对通过缓冲区“传递”插入或删除多个字符的情况进行优化。他们可以通过在缓冲区中维护一个可选的“间隙”并在其中移动字符来实现这一点。 (IIRC,emacs 以这种方式实现其文本缓冲区。)这种方法的问题是:

  • 它需要更多的空间,用于表示间隙位置的属性以及间隙本身。
  • 它使代码变得更加复杂,并减慢了其他操作的速度。例如,charAt在获取字符数组元素之前,必须将offset与间隙的起点和终点进行比较,并对实际索引值进行相应的调整。
  • 仅当应用程序在同一缓冲区上执行多次插入/删除时,它才会有帮助。

毫不奇怪,这种“优化”尚未在标准 StringBuilder / StringBuffer 类中实现。但是,自定义 CharSequence 类可以使用此方法。

The charAt method is O(1).

The deleteCharAt method on StringBuilder and StringBuffer is O(N) on average, assuming you are deleting a random character from an N character StringBuffer / 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 be O(1).

There is no deleteCharAt method for String.


In theory, StringBuilder and StringBuffer 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:

  • It requires more space, for the attributes that say where the gap is, and for the gap itself.
  • It makes the code a lot more complicated, and slows down other operations. For instance, charAt would have to compare the offset 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.
  • It is only going to help if the application does multiple inserts / deletes on the same buffer.

Not surprisingly, this "optimization" has not been implemented in the standard StringBuilder / StringBuffer classes. However, a custom CharSequence class could use this approach.

猫腻 2024-11-24 07:26:49

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.

野生奥特曼 2024-11-24 07:26:49

因为我们都知道字符串在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 of charAt should be int O(1). As other arrays, the delete operation has the O(n) time complexity.

╭⌒浅淡时光〆 2024-11-24 07:26:49

以上所有回复的摘要:
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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文