1. 引言
操作集合不仅仅是遍历、过滤和排序。其实经常会遇到要修改或者重做这些集合,让它们更适合你的需求:比如把字符串集合变成数字集合、从列表里去掉重复项,或者直接换个数据结构。
这些事都能用集合的标准方法、简单循环和基础算法搞定。虽然比LINQ写起来长点,但这样更透明,也有助于你理解集合的工作原理。
修改集合的主要方式
改集合的方法挺多,最常用的其实就这几种:
- 内容修改:添加、删除或者替换元素。
- 元素转换:拿原始集合,基于它生成一个新的——结构、类型或者形式都可以变(比如从List<int>变成List<string>)。
- 改变表现形式:重构集合,比如排序、反转(reverse)或者分组。
我们主要关注前两种,因为排序在另一节课里已经详细讲过了。
可变集合和不可变集合:一个重要细节
要记住,.NET里有些集合可以随便改(比如List<T>),有些就不行(IReadOnlyList<T>、只读修饰的数组等等)。
- 修改——直接改原集合(比如用Add、Remove、Clear等方法)。
- 转换——不动原集合,生成一个新集合,通常是新建一个列表,然后把原集合的元素加进去。
2. 集合内容的修改
所有实现了ICollection<T>的集合,都有基础的方法来改内容。我们用书籍目录举个例子。
添加和删除元素
public class Book
{
public string Title { get; set; }
public string Author { get; set; }
public int Year { get; set; }
}
List<Book> books = new List<Book>
{
new Book { Title = "代码整洁之道", Author = "罗伯特·马丁", Year = 2008 },
new Book { Title = "CLR via C#", Author = "杰弗瑞·里克特", Year = 2012 }
};
// 添加新书
books.Add(new Book { Title = "Head First C#", Author = "安德鲁·斯特尔曼", Year = 2010 });
// 按条件删除书(比如按作者)
for (int i = books.Count - 1; i >= 0; i--)
{
if (books[i].Author == "罗伯特·马丁")
books.RemoveAt(i);
}
// 删除指定对象(如果在列表里有)
Book someBook = books[0];
books.Remove(someBook);
// 清空整个集合
books.Clear();
// 在指定位置插入元素
books.Insert(0, new Book { Title = "Pro C# 9", Author = "安德鲁·特罗尔森", Year = 2021 });
替换和更新元素
比如,有人把出版年份写错了,怎么改?
// 用循环找到那本书,改年份
for (int i = 0; i < books.Count; i++)
{
if (books[i].Title == "Head First C#")
{
books[i].Year = 2018; // 改了年份
break;
}
}
3. 集合的转换
元素类型转换(比如从Book变成字符串)
获取所有书名的集合:
List<string> bookTitles = new List<string>();
foreach (Book book in books)
{
bookTitles.Add(book.Title);
}
foreach (var title in bookTitles)
{
Console.WriteLine(title);
}
获取所有书的年份:
List<int> bookYears = new List<int>();
foreach (Book book in books)
{
bookYears.Add(book.Year);
}
转换成新类型/结构:
public class BriefBookInfo
{
public string Title;
public int Year;
}
List<BriefBookInfo> briefInfos = new List<BriefBookInfo>();
foreach (Book book in books)
{
briefInfos.Add(new BriefBookInfo { Title = book.Title, Year = book.Year });
}
foreach (var info in briefInfos)
{
Console.WriteLine($"{info.Title} ({info.Year})");
}
把集合的集合拍平成一个(flat map)
如果一本书有标签列表,怎么拿到所有标签的总列表:
public class Book
{
public string Title { get; set; }
public List<string> Tags { get; set; }
}
List<Book> booksWithTags = new List<Book>
{
new Book { Title = "代码整洁之道", Tags = new List<string> { "Clean Code", "Refactoring" } },
new Book { Title = "CLR via C#", Tags = new List<string> { "CLR", "Internals" } }
};
List<string> allTags = new List<string>();
foreach (var book in booksWithTags)
{
foreach (var tag in book.Tags)
{
allTags.Add(tag);
}
}
foreach (string tag in allTags)
{
Console.WriteLine(tag);
}
4. 常用的集合转换方法
ToArray
想把列表变成数组:
string[] bookTitlesArray = bookTitles.ToArray();
Distinct——去重
唯一作者(不用LINQ):
List<string> authors = new List<string>();
foreach (Book book in books)
{
if (!authors.Contains(book.Author))
authors.Add(book.Author);
}
Reverse——反转集合
想把顺序反过来:
books.Reverse(); // 直接原地改!
如果不想动原集合:
var reversed = new List<Book>(books);
reversed.Reverse();
排序(详见排序讲座)
books.Sort((a, b) => a.Year.CompareTo(b.Year)); // 按年份排序
按字段分组(模拟GroupBy)
按作者分组书籍(得到“作者—书列表”的字典):
Dictionary<string, List<Book>> booksByAuthor = new Dictionary<string, List<Book>>();
foreach (Book book in books)
{
if (!booksByAuthor.ContainsKey(book.Author))
booksByAuthor[book.Author] = new List<Book>();
booksByAuthor[book.Author].Add(book);
}
foreach (var pair in booksByAuthor)
{
Console.WriteLine($"作者: {pair.Key}");
foreach (var b in pair.Value)
Console.WriteLine($" {b.Title}");
}
5. 有用的小细节
- 用循环做转换,在哪都能用,绝对靠谱。
- 想去重,就用临时集合和Contains/Add方法。
- 别在foreach遍历时改集合!要删很多元素,先单独收集要删的,再删。
6. 常见任务示例
获取某一年以后的书列表
List<Book> recentBooks = new List<Book>();
foreach (Book book in books)
{
if (book.Year > 2010)
recentBooks.Add(book);
}
foreach (var book in recentBooks)
{
Console.WriteLine($"{book.Title} ({book.Year})");
}
输出所有出版年份(去重后升序)
List<int> years = new List<int>();
foreach (Book book in books)
{
if (!years.Contains(book.Year))
years.Add(book.Year);
}
years.Sort();
foreach (int year in years)
Console.WriteLine(year);
准备书籍导出(生成DTO列表)
public class BookExport
{
public string Name;
public string Writer;
public int PublishedYear;
}
List<BookExport> booksForExport = new List<BookExport>();
foreach (Book book in books)
{
booksForExport.Add(new BookExport
{
Name = book.Title,
Writer = book.Author,
PublishedYear = book.Year
});
}
7. 常见错误和坑
像Add、Remove、Clear、Insert和Sort这些方法,都是直接改集合本身的。
如果你想要新集合——就新建一个列表,把需要的元素加进去。
遍历集合时要删元素,记得用倒序遍历(for (int i = Count-1; i >= 0; i--))。
要唯一元素的列表,注意别加重复的:用Contains或者HashSet<T>。
GO TO FULL VERSION