一、 核心
动态数组
二、先理解数组
a. 数组在创建的时候就固定长度。
b. 数组在内存中是 连续分布的一段区域:
elem0\]\[elem1\]\[elem2\]\[elem3\]\[elem4\] ...
c. 内存中数组中的每个元素大小相同。
d. 数组通过指针偏移计算内存地址:
元素地址 = 起始地址 + (i × 单个元素大小)
所以访问 array[i] 只需要:
-
取数组起始地址(已保存)
-
做一次乘法 + 加法
-
直接读取目标位置的值
不需要遍历,也不依赖数组长度
无论访问第 0 个、第 10 个、第 100 万个元素,耗时都一样
因此时间复杂度 = O(1)(常数时间)
三、 再理解动态
数组是固定长度,List为了解决数组不能扩容的问题,动态的意思就是让数组可以扩容,而List就是可以扩容的数组。
- 首先,List中存放元素的核心数据结构还是数组。
cs
// 简化的List<T>核心字段定义
public class List<T>
{
private T[] _items;
// 先省略其他关键字段
}
- 其次,Capacity属性代表List中数组的最大长度。Count属性代表List中数组有效元素的数量。
创建List的时候可以指定初始长度100,100是对Capacity的初始化:
cs
var list = new List<int>(100);
-
当数组满时候出发扩容,扩容的大小默认是对容量进行翻倍。
-
扩容在内存的表现
在堆中开辟一块新的内存,内存大小是原来的2倍,通过Array.Copy将原来的数据复制到新内存的位置,将老的指针指向新的List内存。
所以通常建议在创建数组的时候要设定初始长度,长度是开发者估算使用的,可以有效的避免因为扩容导致的内存移动的消耗。
- Add向尾部新加元素
当数组长度小于Capacity容量的时候添加元素,就不扩容,效率极快。
触发扩容就走4的扩容逻辑。
- Insert插入元素
当数组长度小于Capacity容量的时候添加元素,利用Array.Copy将Insert位置后的所有元素向后一位复制,如果需要扩容就走扩容逻辑,然后再位置放入插入元素。
插入的时间复杂度是O(n),为什么?因为如果插入的位置是第一个位置,那么所有的元素都要向后移动一格,操作的次数就是数组的长度n,所以是O(n)。
四、 删除元素和删除List
List是引用类型,GC只管引用。
- 删除一个元素,元素如果没有被其他类引用,这个元素会等待GC回收。
2.如果List中所有元素都被删除没有被引用,但是List存在引用,List不会被回收。
3.如果List没有被任何引用,List就会被回收。List中的元素和List无关。
五、 对于值类型存储在内存栈上的误区
很多面试题会说值类型存储在栈,引用类型存储在堆,这是不严谨的。
就像一个包含int的Point对象的类型的List,
cs
struct Point { public float x, y; }
List<Point> list = new List<Point>();
list.Add(new Point { x = 1, y = 2 });
list.Add(new Point { x = 3, y = 4 });
内存布局如下:
栈(stack)
┌──────────────────────────┐
│ list 变量(引用)────────────┐
└──────────────────────────┘ │
▼
堆(heap)
┌──────────────────────────────────────────┐
│ List<Point> 对象 │
│ _size = 2 │
│ _items ───────────────────────────────┐ │
│ _version = ... │ │
└──────────────────────────────────────────┘ │
▼
堆(heap)
┌──────────────────────────────────────────┐
│ _items: Point[] 动态数组 │
│ ┌──────┬──────┬──────────────┬──────────┐ │
│ │ P0 │ P1 │ (空位) │ (空位)│ │
│ └──────┴──────┴──────────────┴──────────┘ │
│ P0 = {x = 1, y = 2} │
│ P1 = {x = 3, y = 4} │
└──────────────────────────────────────────┘
那么int值是直接存放在堆中的。
那么这个过程是否存在装箱?
答案是不存在,List存储的是泛型,整个过程没有发生Object和int类型的转化。