List常规使用
List<T>是C#中最常用的集合类型之一,它提供了动态数组的功能,可以存储和管理一组相同类型的元素。面试或许不会考察具体的 API,但是笔试算法的时候有时候会涉及到,避免大家忘记,所以我还是罗列了 List<T>的一些常规使用方法。
使用注意事项
- 预分配容量:如果知道元素的大致数量,初始化时指定容量可以避免多次扩容
- 批量操作:使用
AddRange、RemoveAll等批量操作方法比循环调用单个操作更高效 - 中间插入/删除:在列表中间频繁插入或删除元素会导致性能下降,考虑使用
LinkedList<T> - 值类型与引用类型:List<T>在处理值类型时更高效,避免了装箱拆箱
声明与初始化
List<T>提供了多种初始化方式:
// 1. 空列表初始化
List<int> numbers = new List<int>();
// 2. 带初始容量的初始化
List<string> names = new List<string>(10); // 初始容量为10
// 3. 使用集合初始化器
List<double> prices = new List<double> {1.99, 2.99, 3.99};
// 4. 从数组初始化
int[] array = {1, 2, 3};
List<int> listFromArray = new List<int>(array);
初始化时指定容量可以减少扩容次数,提高性能,特别是当你知道大致元素数量时。
添加元素
List<T>提供了多种添加元素的方法:
List<int> numbers = new List<int>();
// 1. 添加单个元素
numbers.Add(1);
// 2. 添加多个元素
List<int> moreNumbers = new List<int> {2, 3, 4};
numbers.AddRange(moreNumbers);
// 3. 在指定位置插入元素
numbers.Insert(1, 5); // 在索引1处插入5
Add方法在列表末尾添加元素,时间复杂度为O(1)(摊还)。Insert方法在指定位置插入元素,可能需要移动后续元素,时间复杂度为O(n)。
访问与修改元素
可以通过索引访问和修改List<T>中的元素:
List<string> fruits = new List<string> {"apple", "banana", "cherry"};
// 访问元素
string firstFruit = fruits[0]; // "apple"
// 修改元素
fruits[1] = "blueberry"; // 将"banana"改为"blueberry"
索引从0开始,访问不存在的索引会抛出ArgumentOutOfRangeException异常。
删除元素
List<T>提供了多种删除元素的方式:
List<char> letters = new List<char> {'a', 'b', 'c', 'd', 'e'};
// 1. 按值删除(删除第一个匹配项)
letters.Remove('b'); // 删除'b'
// 2. 按索引删除
letters.RemoveAt(1); // 删除索引1处的元素
// 3. 删除范围内元素
letters.RemoveRange(0, 2); // 从索引0开始删除2个元素
// 4. 按条件批量删除
letters.RemoveAll(c => c == 'd'); // 删除所有'd'
// 5. 清空列表
letters.Clear();
Remove方法删除第一个匹配的元素,RemoveAt按索引删除,RemoveAll可以批量删除满足条件的元素。
查找元素
List<T>提供了多种查找方法:
List<int> nums = new List<int> {1, 2, 3, 4, 5, 2};
// 1. 检查是否包含元素
bool hasThree = nums.Contains(3); // true
// 2. 查找元素索引
int firstIndex = nums.IndexOf(2); // 1
int lastIndex = nums.LastIndexOf(2); // 5
// 3. 按条件查找
int firstEven = nums.Find(x => x % 2 == 0); // 2
List<int> allEvens = nums.FindAll(x => x % 2 == 0); // [2, 4, 2]
// 4. 检查是否存在满足条件的元素
bool hasBigNum = nums.Exists(x => x > 10); // false
这些方法使得在列表中查找元素变得非常方便。
遍历列表
有多种方式可以遍历List<T>:
List<int> numbers = new List<int> {1, 2, 3, 4, 5};
// 1. for循环
for (int i = 0; i < numbers.Count; i++) {
Console.WriteLine(numbers[i]);
}
// 2. foreach循环
foreach (int num in numbers) {
Console.WriteLine(num);
}
// 3. ForEach方法
numbers.ForEach(num => Console.WriteLine(num));
// 4. 使用Enumerator
var enumerator = numbers.GetEnumerator();
while (enumerator.MoveNext()) {
Console.WriteLine(enumerator.Current);
}
foreach和ForEach方法更简洁,而for循环在需要索引时更有用。
排序与反转
List<T>内置了排序和反转功能:
List<int> nums = new List<int> {3, 1, 4, 2};
// 1. 默认排序(升序)
nums.Sort(); // [1, 2, 3, 4]
// 2. 自定义排序
nums.Sort((x, y) => y.CompareTo(x)); // 降序 [4, 3, 2, 1]
// 3. 反转顺序
nums.Reverse(); // [1, 2, 3, 4]
Sort方法默认使用元素的IComparable<T>实现,也可以传入自定义比较器。
容量管理
List<T>会自动扩容,但也可以手动管理容量:
List<int> list = new List<int>();
// 1. 获取当前容量
int cap = list.Capacity;
// 2. 设置最小容量
list.EnsureCapacity(100);
// 3. 释放多余容量
list.TrimExcess();
合理管理容量可以减少内存分配和提高性能。
其他常用操作
List<int> list1 = new List<int> {1, 2, 3};
// 1. 转换为数组
int[] array = list1.ToArray();
// 2. 获取范围
List<int> subList = list1.GetRange(1, 2); // [2, 3]
// 3. 复制到数组
int[] target = new int[3];
list1.CopyTo(target); // target = [1, 2, 3]
// 4. 批量设置值
list1.ForEach(x => x * 2); // 每个元素乘以2