集合
C#中,所有集合都定义在system.collection下,并且实现了ICollection接口
Array 数组 固定长度 固定类型
ArrayList 列表 可变长度 任意类型
List<T> 列表 可变长度 固定类型
Dictionary<T> 字典 键值对结构
Queue<T> 队列 先进先出(FIFO)集合
Stack<T> 栈 后进先出(LIFO)集合
IEumerable<T> 可迭代集合
集合的特点 可以储存无限个元素 任何一个集合都支持搜索排序复制添加删除等操作
数组
固定长度,有明确的顺序
string[] daysOfWeek =
{
"Tuesday",
"Monday",
"Wendesday",
"Thursday",
"Friday",
"Saturday",
"Sunday"
};
foreach(var day in daysOfWeek)
{
Console.WriteLine(day);
}
// 零索引 0-indexed 从0开始
Console.WriteLine(daysOfWeek[0]);
Console.WriteLine(daysOfWeek[1]);
Console.WriteLine(daysOfWeek[6]);
// 安全 不会返回不存在的数据
//Console.WriteLine(daysOfWeek[7]);
// 固定长度 可以先声明长度,在赋值
string[] daysOfWeek2 = new string[7];
daysOfWeek2[0] = "Monday";
daysOfWeek2[1] = "Tuesday";
daysOfWeek2[2] = "Wendesday";
daysOfWeek2[3] = "Thursday";
daysOfWeek2[4] = "Friday";
daysOfWeek2[5] = "Saturday";
daysOfWeek2[6] = "Sunday";
列表和ArrayList
list的底层实现任使用数组, 在数据装满后,如还需要添加新数据,list会创建一个新数组代替旧数组,并且复制旧数组的数据。列表也叫动态数组
//列表需要先初始化
List<string> daysOfWeek3 = new List<string>();
daysOfWeek3.Add("Monday");
daysOfWeek3.Add("Tuesday");
daysOfWeek3.Add("Wendesday");
daysOfWeek3.Add("Thursday");
daysOfWeek3.Add("Friday");
List支持泛型,ArrayList不支持泛型,只能保存对象
//ArrayList中可以添加任意类型数据
var array = new ArrayList();
array.Add(1);
array.Add("123");
array.Add(daysOfWeek);
//当从ArrayList中取数据时会涉及到装箱和拆箱的操作,影响性能
对于列表,也通过接口初始化列表
IList<string> daysOfWeek3 = new List<string>();
List的构造器
一个无参,两个有参
//可以传入一个列表或数组(传入一个可迭代的集合)
var daysOfWeek4 = new List<string>(daysOfWeek3);
var daysOfWeek5 = new List<string>(daysOfWeek);
var daysOfWeek6 = new List<string>(7);
// 数量 容量
Console.WriteLine($"{daysOfWeek6.Count} / {daysOfWeek6.Capacity}");// 0/7
//Add
daysOfWeek6.Add("Monday");
daysOfWeek6.Add("Tuesday");
daysOfWeek6.Add("Wendesday");
daysOfWeek6.Add("Thursday");
daysOfWeek6.Add("Friday");
daysOfWeek6.Add("Saturday");
daysOfWeek6.Add("Sunday");
Console.WriteLine($"{daysOfWeek6.Count} / {daysOfWeek6.Capacity}");
daysOfWeek6.Add("Sunday");
Console.WriteLine($"{daysOfWeek6.Count} / {daysOfWeek6.Capacity}");
// Add AddRange
daysOfWeek7.AddRange(daysOfWeek); //array
daysOfWeek7.AddRange(daysOfWeek3); //List
Console.WriteLine($"{daysOfWeek7.Count} / {daysOfWeek7.Capacity}");
// Insert InsertRange 向指定位置添加元素
daysOfWeek7.InsertRange(2, daysOfWeek);
daysOfWeek7.Insert(2, "随便");
Console.WriteLine(string.Join(", ", daysOfWeek7));
// 删除数据 RemoveAt RemoveRange
daysOfWeek7.RemoveAt(0);
daysOfWeek7.RemoveAt(2);
daysOfWeek7.RemoveRange(2, 6);
//删除找到的第一个元素
daysOfWeek7.Remove("Monday");
//删除所有符合条件(lambda表达式)的元素
daysOfWeek7.RemoveAll( i => i == "Monday");
列表中插入数据:先将列表从指定位置拆开,然后加入数据,再将三段数据整合,故性能很差。删除数据同理
迭代器
// 读取数据
var a = daysOfWeek6.Count;//读取大小
var b = daysOfWeek6.Capacity;//读取容量
// 索引器
var c = daysOfWeek6[3];//读取某个成员
// 迭代器
List<string>.Enumerator enumerator = daysOfWeek6.GetEnumerator();
enumerator.Current//指向的当前元素
enumerator.MoveNext()//改变current的指向,当current指向最后元素会返回false
//current的第一次和最后一次指向都为空,解决这个问题的方法就是使用foreach(故foreach的底层是一个while循环)
foreach (var day in daysOfWeek6)
{
//day = "some day";
Console.WriteLine(day);
//daysOfWeek6.Add("one day");
}
foreach不能在循环中改变数组的长度,因为使用迭代器遍历集合的过程中,集合的长度不允许被改变
//day = "some day";foreach循环中数据全部为只读数据,不能被修改但是引用类型的变量值可以被修改
IEnumerable 和 IEnumerator
IEnumerable接口的工作就是提供迭代 几乎所有的集合都实现了IEnumerable,所以可以用foreach循环遍历
//实现IEnumerable接口
public class MyList<T> : IEnumerable<T>
{
private T[] _data;
int currentIndex;
public MyList(int length)
{
this._data = new T[length];
currentIndex = 0;
}
public void Add(T obj)
{
_data[currentIndex] = obj;
//currentIndex = currentIndex + 1;
currentIndex++;
}
public IEnumerator<T> GetEnumerator()
{
return new MyEnumerator<T>(_data);
}
IEnumerator IEnumerable.GetEnumerator()
{
throw new NotImplementedException();
}
//MyEnumerator的部分
public class MyEnumerator<T> : IEnumerator<T>
{
T[] _data;
int _position = -1;
public MyEnumerator(T[] data)
{
_data = data;
}
public T Current { get => _data[_position]; }
object IEnumerator.Current { get => Current; }
public void Dispose()
{
}
public bool MoveNext()
{
_position = _position + 1;
return _position < _data.Length;
}
public void Reset()
{
_position = -1;
}
}
迭代与yield return
yield类似于懒加载,除非数据被使用,否则yield返回的数据不会被创建 yield并不返回数据,而是返回数据的迭代
var customers = GetCustomersYield(1000000);
foreach (var c in customers)
{
if (c.Id < 3)
{
Console.WriteLine($"客户id {c.Id}, 客户姓名: {c.Name}");
}
else
{
break;
}
}
static IEnumerable<Customer> GetCustomersYield(int count)
{
for (int i = 0; i < count; i++)
{
yield return new Customer(i, $"a{i}", "广州");
//yield只运行了三次,与if执行次数一致
}
}
字典和哈希表
字典与哈希表几乎无区别,字典有泛型哈希表无泛型
var customers = GetCustomersDictionary(1000000);
var customer = customers[999999];
Console.WriteLine($"客户id {customer.Id}, 客户姓名: {customer.Name}");
//hashtable 哈希表
var customerHashtable = GetCustomersHashtable(1000000);
var customer2 = (Customer)customerHashtable[999999];
//此处取得的对象是object类型,需要做类型转换,如果涉及到引用数据类型和值数据类型转换,同样有装箱和拆箱的问题
Console.WriteLine($"客户id {customer2.Id}, 客户姓名: {customer2.Name}");
Console.Read();
}
static Hashtable GetCustomersHashtable(int count)
{
var customers = new Hashtable();
for (int i = 0; i < count; i++)
{
customers.Add(i, new Customer(i, $"a{i}", "广州"));
}
return customers;
}
static Dictionary<int, Customer> GetCustomersDictionary(int count)
{
var customers = new Dictionary<int, Customer>();
for (int i = 0; i < count; i++)
{
customers.Add(i, new Customer(i, $"a{i}", "广州"));
}
return customers;
}
集合的交、并、差运算 HashSet
HashSet<int> numbers1;
HashSet<int> numbers2;
//分别进行numbers1和numbers2的值初始化或赋值
numbers1.UnionWith(numbers2);//求两个集合的并集。
HashSet<int> numbers1;
HashSet<int> numbers2;
//分别进行numbers1和numbers2的值初始化或赋值
numbers1.IntersectWith(numbers2);//求两个集合的交集。
HashSet<int> numbers1;
HashSet<int> numbers2;
//分别进行numbers1和numbers2的值初始化或赋值
numbers1.ExceptWith(numbers2);//求两个集合的差集。
HashSet<int> numbers1;
HashSet<int> numbers2;
//分别进行numbers1和numbers2的值初始化或赋值
numbers1.SymmetricExceptWith(numbers2);//求两个集合的对称差集。