【问题标题】:Why does the StringBuffer class uses an Array as it's underlying data structure instead of a LinkedList?为什么 StringBuffer 类使用 Array 作为其底层数据结构而不是 LinkedList?
【发布时间】:2019-02-14 08:50:01
【问题描述】:

Java 中的StringBuffer/StringBuilder 类主要用于修改字符串值,而不必每次都初始化一个新的字符串对象。

是否有特定原因,它没有使用 LinkedList 而不是 Char 数组,因为它是底层数据结构?

将 char 插入 Array 将始终导致 O(n) 时间将所有元素复制到下一个索引,而在 LinkedList 的情况下在 O(1) 时间内完成。

【问题讨论】:

  • Inserting a char into a Array will always result in a O(n) time to copy all elements to the next index 什么? where as that that be done in O(1) time in case of a LinkedList. 是的,每个字符的开销约为 3000%。

标签: java arrays linked-list stringbuilder stringbuffer


【解决方案1】:

随机访问

StringBuilder 具有随机访问操作,例如charAtsubstring,使用链表会非常慢。

插入

事实上,数组列表并不比链表特别慢,即使涉及到插入等其他操作。通常StringBuilder 不会用于创建一百万个字符的字符串,因此我们不太可能需要多次调整缓冲区大小。

  • 最后

我必须纠正你,插入在末尾​​strong>总是需要O(n) 元素的副本。最坏的情况确实是O(n),但摊销复杂度是O(1),因为我们不只是一次分配一个元素。当数组不够大而无法进行另一次插入时,大多数实现都会将数组的大小加倍。

  • 中间

中间的插入总是需要插入右侧的元素副本,所以是的,它非常慢,但它不是StringBuilder 的典型用例,大多数插入发生在末尾。此外,链表在中间插入时具有相同的平均复杂度,因为它们首先必须通过迭代链表到达正确的节点。

数据局部性

与链表相比,数组的另一个优势是数据局部性。数组列表比链表迭代更快,因为当处理器围绕数组元素加载一块内存时,它也会缓存该元素的一些邻居,因此返回速度更快。另一方面,链表的所有元素都可能位于非常遥远的内存位置,这对缓存不友好。

内存占用

因为每次调整大小时我们都会将数组大小翻倍,所以动态数组可能会占用相当大的内存(但至少我们不需要过于频繁地复制元素)。链表也有相当大的内存占用,因为它们需要一个额外的引用和每个元素的指针,而元素紧凑地存储在数组中。平均而言,我会说典型的数组列表比链表具有更小的内存占用,但我可能错了。对于原始类型尤其如此 - 例如char - 因为链表需要包装器对象(至少在 Java 中,因为没有指针),而我们可以使用更紧凑的原始数组。

最后的笔记

最后,我在回答中使用了StringBuilder 而不是StringBuffer,因为这是大多数用例的推荐实现。 StringBuffer 仅在线程安全是硬性要求时才可取。否则,StringBuilder 的性能会更好。

PS: Python 最突出的数据结构是list,猜猜看……它是用动态数组实现的!可调整大小的数组通常是比链表更好的选择。链表性能明显更高的唯一情况是应用程序关注靠近链表头部的元素并在该区域频繁插入或删除。

【讨论】:

  • 哇...感谢您的详细回答。我现在明白了。我认为 LinkedList 唯一会更快的地方是向开头或中间任何地方插入,但是,这个优势被抵消了,因为我们现在必须从头部一个接一个地遍历该节点。
猜你喜欢
  • 1970-01-01
  • 2014-06-26
  • 2010-09-28
  • 1970-01-01
  • 1970-01-01
  • 2010-10-29
  • 1970-01-01
  • 2014-08-05
相关资源
最近更新 更多