Java CharAt()和deleteCharAt()性能

时间:2011-06-23 22:32:15

标签: java performance string complexity-theory stringbuilder

我一直想知道java中String / StringBuilder / StringBuffer的charAt函数的实现 那是什么样的复杂性? 还有什么关于StringBuffer / StringBuilder中的deleteCharAt()?

5 个答案:

答案 0 :(得分:26)

对于StringStringBufferStringBuildercharAt()是一项固定时间操作。

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

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

答案 1 :(得分:16)

让我们依次查看每个方法的相应实际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略有不同(猜猜为什么?)。仔细看看这些方法实现本身就会给我们带来其他的细微差别。

现在,让我们看一下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)时间复杂度。

答案 2 :(得分:7)

charAt方法为O(1)

假设您要删除deleteCharAt字符StringBuilder中的随机字符,StringBufferO(N)上的N方法平均为StringBuffer } / StringBuilder。 (平均而言,它必须移动剩余字符的一半以填充被删除字符留下的“洞”。没有多次操作的摊销;见下文。)但是,如果你删除最后一个字符,费用为O(1)

deleteCharAt没有String方法。


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

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

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

答案 3 :(得分:5)

charAt超快(并且可以使用String的内在函数),它是一个简单的数组索引。 <{1}}需要一个arraycopy,因此删除一个char将不会很快。

答案 4 :(得分:0)

由于我们都知道该字符串在JDK中作为字符数组实现,因此实现了randomAccess接口。因此charAt的时间复杂度应为int O(1)。与其他阵列一样,删除操作的时间复杂度为O(n)