概述
集合本质上是一个类,最起码实现IEnumerable<T>或非泛型类型 IEnumerable
其他集合接口有:
IList<T>:按照特定顺序存储值并按位置访问它们。
IDictionary<TKey,TValue>:键及其关联的值的无序列表。
ICollection<T>:Count属性返回元素计数,CopyTo()方法允许将集合转换成数组。
C#中的集合包括:Stack、Queue、List、Hashtable、Dictionary等。
在List<T>中查找特定元素可以使用Contains() (返回bool值),IndexOf()(返回第一个索引),LastIndexOf()(返回最后一个索引),还可以使用 BinarySearch()
BinarySearch() 采用二叉搜索算法,但要求元素已排好序。如果事先没有排好序,即使元素存在也可能找不到。BinarySearch()一个有用的功能是,假如没有找到指定的元素,会返回一个负整数。该值的按位取反(~)结果是“大于被查找元素的下一个元素”的索引,如果没有更大的值,就返回(~元素总数)
List<string> list = new List<string>() { "private","protect","public"};
list.Sort();
int search = list.BinarySearch("protect internal");
if(search<0)
{
list.Insert(~search, "protect internal");
}
foreach (var item in list)
{
Console.Write(item+", ");
}
private, protect, protect internal, public,
1、集合初始化器
在集合实例化期间用一组初始成员构造该集合
List<string> list = new List<string>() { "a", "b", "c" };
或:
List<string> list = new List<string> { "a", "b", "c" };
字典的初始化语法稍复杂点:
//C# 6.0 or later
Dictionary<string, ConsoleColor> colorMap1 = new Dictionary<string, ConsoleColor>
{
["a"]=ConsoleColor.Green,
["b"]=ConsoleColor.Blue,
["c"]=ConsoleColor.Red,
};
//before C#6.0
Dictionary<string, ConsoleColor> colorMap2 = new Dictionary<string, ConsoleColor>
{
{"a",ConsoleColor.Red},
{"b",ConsoleColor.Green},
{"c",ConsoleColor.Blue},
};
2、foreach
foreach使用迭代器模式遍历集合,不依赖索引,不需要事先知道集合中有多少元素
编译器禁止对foreach变量赋值。在foreach执行期间,集合中的元素计数不能变,集合项本身也不能更改
3、标准查询操作符
(1)Where:筛选
输出是一个新的 IEnumerable<T>集合
List<string> list1 = new List<string> { "aa","abc","bc"};
List<string> list2 = list1.Where(item => item.StartsWith("a")).ToList();
list2.ForEach(item => Console.WriteLine(item));
aa
abc
(2)Select:投射
可以对集合中的每个数据项进行类型转换
//投射成FileInfo
IEnumerable<string> fileList = Directory.GetFiles("D:");
IEnumerable<FileInfo> fileInfos = fileList.Select(file => new FileInfo(file));
//投射成元组
IEnumerable<string> fileNames = Directory.EnumerateFiles("D:");
IEnumerable<(string fileName, int size)> items = fileList.Select(file =>
{
FileInfo fileInfo = new FileInfo(file);
return (fileName: fileInfo.Name, size: file.Length);
});
(3)Count:计数
List<string> list1 = new List<string> { "aa","abc","bc"};
var count=list1.Count;
Console.WriteLine(list1.Count(item => item.StartsWith("a")).ToString());
2
如果计数只是看个数是否大于0,优先选用 Enumerable.Any(),Count()会遍历集合,Any()只尝试遍历集合中的一个项,成功就返回true,而不是遍历整个序列。
4、延迟执行
linq有一个重要的概念是延迟执行,即lambda表达式在声明时不执行,在调用时才开始执行。
查询对象代表的是查询而非结果。每次调用查询对象都会执行查询,因为查询对象不确定上次执行的结果现在是否发生改变。
如果要避免反复执行,一次查询执行后,可将结果缓存起来,如使用ToXXX(ToArray()等)方法把结果赋值给一个变量,对赋值的变量进行遍历,就不再涉及查询表达式。
IEnumerable<string> list = new List<string> { "aa","abc","bc"};
bool result=false;
list = list.Where(item =>
{
//谓词通常只能做一件事情:对一个条件进行求值。它不应该有任何"副作用",包括打印
if (result = item.StartsWith("a"))
{
Console.WriteLine("t" + item);
}
return result;
});
//foreach触发Lambda表达式执行
Console.WriteLine("1、 the result is:");
foreach (var item in list)
{
}
Console.WriteLine();
//调用Count()会为每一项触发Lambda表达式
Console.WriteLine("2、 the result is:");
Console.WriteLine(list.Count());
Console.WriteLine();
//调用ToList()、ToArray()、ToDictionary()、ToLookup()会为每一项触发Lambda表达式
Console.WriteLine("3、 the result is:");
Console.WriteLine(list.ToList());
5、使用OrderBy和ThenBy进行排序
OrderBy() 获取一个Lambda表达式,该表达式标识了排序要依据的键。
开始访问集合成员时排序才会生效。除非拿到所有需要排序的项,否则无法排序,因为无法确定是否已获得第一项。
要为一次排序后的列表继续排序需要调用ThenBy() 。
重复调用OrderBy() 会冲掉前面OrderBy()的结果。多个OrderBy() 共存时只有最后一个生效。
6、Join内部联接(交集)
在客户端,对象和对象的关系一般已经建立好了,但从非对象存储加载的数据通常不是这种情况。这些数据需要联接到一起,以便以适合数据的方式从一种对象类型切换到另一种。
内部连接也成为同等连接,因为它们基于同等的键求值——只有在两个集合中都有对象,它们的记录才会出现在结果集中。
示例数据:
internal class Department
{
public long Id { get; set; }
public string Name { get; set; }
public override string ToString()
{
return Name;
}
}
internal class Employee
{
public int Id { get; set; }
public string Name { get; set; }
public string Title { get; set; }
public int DepartmentId { get; set; }
public override string ToString()
{
return $"{Name} ({Title})";
}
}
internal static class CorporateData
{
public static readonly Department[] Departments = new Department[]
{
new Department() { Id = 0, Name = "Corporate" },
new Department() { Id = 1, Name = "Engineer" },
new Department() { Id = 2, Name = "Store" },
new Department() { Id = 3, Name = "IT" },
new Department() { Id = 4, Name = "HR" },
};
public static readonly Employee[] Employees = new Employee[]
{
new Employee{Name="Mark",Title="CEO",DepartmentId=0},
new Employee{Name="Michael",Title="CFO",DepartmentId=4},
new Employee{Name="Brian",Title="CF",DepartmentId=3},
new Employee{Name="Ann",Title="BE",DepartmentId=4},
new Employee{Name="Pat",Title="LA",DepartmentId=2},
new Employee{Name="Kevin",Title="XIA",DepartmentId=1},
new Employee{Name="Thomas",Title="CIS",DepartmentId=3},
new Employee{Name="Eric",Title="SOS",DepartmentId=4},
};
}
public static void Main(string[] args)
{
IEnumerable<Department> departments = CorporateData.Departments;
Print(departments);
IEnumerable<Employee> employees = CorporateData.Employees;
Print(employees);
}
private static void Print<T>(IEnumerable<T> items)
{
foreach (var item in items)
{
Console.WriteLine(item);
}
Console.WriteLine("----------------------");
}
Corporate
Engineer
Store
IT
HR
----------------------
Mark(CEO)
Michael(CFO)
Brian(CF)
Ann(BE)
Pat(LA)
Kevin(XIA)
Thomas(CIS)
Eric(SOS)
----------------------
Join连接:
//Join() 的第一个参数为inner,指定employees要联结到的集合
//接着两个参数都是lambda表达式,outerKeySelector和innerKeySelector,为两组数据指定键
//最后一个参数指定最终要选择的结果项
IEnumerable<(int Id,string Name,string Title,Department Department)> employeeItems = employees.Join(departments,
employee => employee.DepartmentId,
department => department.Id,
(employee, department) => (employee.Id, employee.Name,employee.Title, department));
foreach (var item in employeeItems )
{
Console.WriteLine($"{item.Name} ({item.Title})"+" "+ item.Department);
}
Console.WriteLine();
//反向连接
IEnumerable<(long Id, string Name, Employee Employee)> departmentItems = departments.Join(employees,
department => department.Id,
employee => employee.DepartmentId,
(department, employee) => (department.Id, department.Name, employee));
foreach (var item in departmentItems)
{
Console.WriteLine(item.Name + " " + item.Employee);
}
Mark (CEO) Corporate
Michael (CFO) HR
Brian (CF) IT
Ann (BE) HR
Pat (LA) Store
Kevin (XIA) Engineer
Thomas (CIS) IT
Eric (SOS) HRCorporate Mark(CEO)
Engineer Kevin(XIA)
Store Pat(LA)
IT Brian(CF)
IT Thomas(CIS)
HR Michael(CFO)
HR Ann(BE)
HR Eric(SOS)
7、Groupby分组
使用GroupBy() 返回 IEnumerable<IGrouping<TKey, TSource>>类型的数据,该类型有一个属性Key 指定了作为分组依据的键。可以用foreach语句枚举组中的项,或者讲数据聚合计数
IEnumerable<IGrouping<int,Employee>> groupedEmployees=employees.GroupBy(employee => employee.DepartmentId);
foreach (var group in groupedEmployees)
{
foreach (var item in group)
{
Console.WriteLine(item.DepartmentId + " " + item.Name);
}
}
0 Mark
4 Michael
4 Ann
4 Eric3 Brian
3 Thomas2 Pat
1 Kevin
8、GroupJoin一对多
IEnumerable<(long Id, string Name, IEnumerable<Employee> Employees)> groupJoinItems = departments.GroupJoin(employees,
department => department.Id,
employee => employee.DepartmentId,
(department, departmentEmployees) => (department.Id, department.Name, departmentEmployees));
foreach (var group in groupJoinItems)
{
Console.WriteLine(group.Name);
foreach (var item in group.Employees)
{
Console.WriteLine(item);
}
Console.WriteLine();
}
Corporate
Mark (CEO)Engineer
Kevin (XIA)Store
Pat (LA)IT
Brian (CF)
Thomas (CIS)HR
Michael (CFO)
Ann (BE)
Eric (SOS)
外部联接:某些情况下,即使对应的对象不存在,也有必要创建一条记录,这时候,可以通过组合使用GroupJoin()、SelectMany() 和DefaultIfEmpty()来实现(左)外部联接。
IEnumerable<Department> departments = CorporateData.Departments;
IEnumerable<Employee> employees = CorporateData.Employees;
var items = departments.GroupJoin(employees,
departments => departments.Id,
employees => employees.DepartmentId,
(department, departmentEmployees) => new
{
department.Id,
department.Name,
Employees = departmentEmployees
}).SelectMany(departmentRecord => departmentRecord.Employees.DefaultIfEmpty(),
(departmentRecord, employee) => new
{
departmentRecord.Id,
departmentRecord.Name,
Employees=departmentRecord.Employees
}).Distinct();
foreach (var item in items)
{
Console.WriteLine(item.Name);
foreach (var employee in item.Employees)
{
Console.WriteLine(employee);
}
Console.WriteLine();
}
Corporate
Mark (CEO)
Engineer
Kevin (XIA)
Store
Pat (LA)
IT
Brian (CF)
Thomas (CIS)
HR
Michael (CFO)
Ann (BE)
Eric (SOS)
9、SelectMany
SelectMany() 处理集合的集合。
Select() 可以一边投射一边转换,但项的数量不会发生改变。而SelectMany() 遍历由Lambda表达式标识的每一项,并将每一项都放到一个新的集合,新集合整合了子集合中的所有项。
(string Team, string[] Players)[] worldCup2006Finalists= new[]
{
(
TeamName:"France",
Players:new string[]
{
"Fabin","Fabin","Fabin",
}
),
(
TeamName:"France",
Players:new string[]
{
"Buffon","Buffon","Buffon"
}
)
};
IEnumerable<string> players= worldCup2006Finalists.SelectMany(team=>team.Players);
foreach (var item in players)
{
Console.Write(item+" ");
}
Fabin Fabin Fabin Buffon Buffon Buffon
10、一些简单的查询操作符
OfType:只返回特定类型的项
Union:合并两个集合,生成两个集合中的所有项的超集。结果集合不包含重复的项。
Concat:合并两个集合,生成两个集合中的所有项的超集,包含重复项。
Intersect:在结果集合中填充两个原始集合中所有的项
Distinct:筛选掉集合中重复的项
SequenceEquals:比较两个集合,返回bool值,指出集合是否一致,项的顺序也作为比较依据
Reverse:反转集合中各项的顺序。
11、匿名类型
匿名类型没有名称,但它仍是强类型的。
利用匿名类型可以对任意对象执行Select() 操作,只提取原始集合中满足当前算法的内容,而不必声明一个新类来专门包含这些内容。
编译器在生成匿名类型的代码时会重写ToString() 方法,对输出进行格式化,输出匿名类型会自动列出属性名及其值。类似的,生成的类型还会重写 Equals() 和 GetHashCode() 的实现。因此,一旦属性的顺序发生了变化,就会生成一个全新的数据类型。匿名类型一经实例化,再更改它的某个属性,就会造成编译错误。
IEnumerable<string> fileList = Directory.EnumerateFiles("D:");
var files = fileList.Select(file =>
{
FileInfo fileInfo = new FileInfo(file);
return new
{
FileName = fileInfo.Name,
Size = fileInfo.Length
};
});
Print(files);
{ FileName = SFGH.docx, Size = 13263 }
匿名类型不能使用集合初始化器,因为集合初始化器要求执行一次构造函数调用,但根本没办法命名匿名类型的构造函数。
最后
以上就是靓丽绿茶为你收集整理的【C#】集合的全部内容,希望文章能够帮你解决【C#】集合所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复