为何ArrayList与List添加千万整数后赋值耗时一致?
Great question—let's unpack what's happening here with both the initial add loop and the subsequent assignment loop.
First, Clarify the Core Difference Between ArrayList and List
ArrayListis a non-generic collection that storesobjectreferences. When you add anint(a value type) to it, every addition requires boxing: converting the value-typeintinto anobjectstored on the heap. This involves heap memory allocation and object instance creation—expensive operations when repeated 10 million times.List<int>is a generic value-type collection. Its internal storage is a plainint[]array, so adding anintjust copies the value directly into the array with no boxing or extra memory allocations. That's why the first loop is drastically faster forList<int>.
Why the Assignment Loop Has Similar Performance?
Your observation about similar timing in the second loop makes sense once you break down what's actually happening, and no—the boxing for the assignment loop isn't done beforehand. Here's why:
1. No Collection Overhead from Resizing
The biggest performance hit in the first loop isn't just boxing—it's collection resizing. Both ArrayList and List<int> start with a small default capacity (usually 4 elements). Every time the collection reaches its capacity, it has to allocate a new larger array and copy all existing elements over. For 10 million elements, this happens dozens of times, and the copy operations add significant overhead.
In the second loop, both collections are already at full capacity (10 million elements), so there's no resizing or array copying involved. This removes a major source of performance difference between the two.
2. Boxing Overhead in the Assignment Loop Is Less Impactful
When you assign a new int to an ArrayList index (arrayList[i] = newValue), you do still need to box the new int value. However:
- Unlike the first loop, there's no repeated memory allocation overhead from resizing to amplify the cost of boxing.
- The .NET JIT compiler optimizes repeated boxing operations in loops. While it can't eliminate boxing entirely for unique values, it generates more efficient code that reduces the per-operation overhead compared to the first loop (where boxing was combined with resizing and other collection housekeeping).
3. The Assignment Operation Itself Is Comparable
For List<int>, assigning a value is a simple write to an array element—super fast. For ArrayList, assigning involves boxing the new int and writing a reference to the internal object[] array. While boxing has cost, writing a reference is nearly as fast as writing a value type directly. When you remove the resizing overhead from the equation, these two operations end up being much closer in speed.
Quick Correction to Your Assumption
You mentioned wondering if "ArrayList's integer boxing/unboxing was completed before the second loop"—that's not the case. The second loop is assigning new values (like i * 2), which are distinct from the initial values added in the first loop. Each new value requires its own boxing operation when stored in the ArrayList. The similarity in timing comes from the removal of resizing overhead and JIT optimizations, not pre-completed boxing.
内容的提问来源于stack exchange,提问作者ElConrado

