Scala中Array与ArrayBuffer的适用场景及性能相关疑问
Great question—let’s break this down clearly, since choosing between these two often comes down to balancing performance and flexibility in your Scala code.
Core Tradeoffs First
First, let’s recap the fundamental differences that drive their use cases:
Array: Fixed-length, contiguous memory block. Random access is O(1), and it’s very memory-efficient because there’s no extra overhead for dynamic resizing. However, modifying its size (adding/removing elements) requires copying the entire array, which is an O(n) operation every time.ArrayBuffer: Variable-length, backed by an underlying array with reserved "extra" space. Adding elements to the end is amortized O(1)—most of the time it’s instant, and only when the reserved space runs out does it copy the underlying array (a one-time O(n) cost that gets averaged out over many operations).
Common Consensus on Scenarios
The general rule of thumb holds up for most cases:
- Use
Arraywhen you know the exact length upfront and don’t need to modify the size later. This is where it shines—no resizing overhead, and for built-in types likeIntorString, the contiguous memory plays nicely with CPU caching, making traversals and random access faster thanArrayBuffer. - Use
ArrayBufferwhen the length is unknown, or you need to frequently add/remove elements (especially from the end). Its dynamic resizing handles these operations far more efficiently than manually managingArraycopies.
Your Specific Confusion: Array Efficiency with Built-in Types and :+=
Let’s clear this up: The claim that Array is more efficient for built-in types only applies to fixed-length scenarios.
If you’re using :+= on an Array, you’re not actually modifying the original array—Scala implicitly converts it to a WrappedArray, which creates a new array every time you call :+= (copying all existing elements to the new array). This is O(n) per operation, which gets extremely slow as your dataset grows. For dynamic addition, even with built-in types, ArrayBuffer is way more efficient.
Built-in vs. Non-Built-in Types
The type (built-in vs. custom) doesn’t change the core rule:
- For fixed-length, no-resize scenarios:
Arrayis slightly more efficient for any type (built-in or not) because it avoids the small overhead ofArrayBuffer’s wrapper layer. The caching benefit is just extra for built-in primitives. - For dynamic scenarios (unknown length, adding/removing elements):
ArrayBufferis better regardless of the type. The resizing mechanism works the same way, and you avoid the O(n) copy cost of modifying anArray.
Matrix Use Case: Adding Rows
If you need to dynamically add rows to a matrix, ArrayBuffer[Array[Int]] is the optimal choice—not Array[Array[Int]].
Here’s why: Array[Array[Int]] is a fixed-length array of arrays. Every time you add a row with :+, you’re creating a new outer array and copying all existing rows into it—an O(k) operation where k is the number of rows. With ArrayBuffer[Array[Int]], adding a row to the end is amortized O(1), which is drastically more efficient as the number of rows grows.
If your rows also need to be dynamically resized (e.g., adding elements to individual rows), you can go a step further and use ArrayBuffer[ArrayBuffer[Int]] for full flexibility.
内容的提问来源于stack exchange,提问作者osk

