CSDN代码块不支持LINQPAD8的写法,换了C#和csharp都试过,不过输出结果基本有注释,没注释的话一般很简单能看出来输出结果

1.C#各类集合详解

  • C# 集合是一个四层接口体系:IEnumerable(只能遍历)→ ICollection(能增删)→ IList/IDictionary<TKey,TValue>(具体功能)→ 具体实现类。

  • 需要注意的是,大部分线程都是不安全的,单线程可以阻止集合为空,多线程操作时如果集合为空可能会抛出异常终止程序运行

  • ICollection:

    • IList:
      • Array(连续不可变空间)
      • ArrayList(连续可变空间)
      • List
      • LinkedList(双向链表)
    • ISet:
      • HashSet
      • TreeSet
  • IMap:

    • HashMap
    • TreeMap

1.1 Array

  • 连续不可变空间
  • 数组元素必须一致
  • 数组查找速度很快,新增和删除速度很慢
  • 在初始化时要指定长度,或元素能够推断,或者说不提供初始值时必须指定长度

以下代码在LINQPad8运行:

void Main()
{
	TestArray(); // 调用方法
}

void TestArray()
{
	int[] num1 = new int[] { 1, 2, 3, 4, 5 };
	num1.Dump();
	num1.Length.Dump(); //输出长度
	num1[1].Dump(); //输出数组的第二个数(数组从0计数)
	num1.Reverse().Dump(); //反转数组
	Array.Clear(num1, 1, 2); //将设定的数组元素变为默认值
	num1.Dump();
	
	int[] numSort = new int[] { 3, 4, 1, 2, 5 };
	Array.Sort(numSort); //对数组进行排序,要注意会影响到源数组
	numSort.Dump();

	string[] num2 = new string[2] { "error", "Umbrella" };
	num2.Dump();

	int[][] num3 = new int[2][] {new int[2]{ 1, 2 }, new int[3]{ 1, 3, 4 } }; 
	//这是包含2个子数组的锯齿数组(Jagged Array),第一个子数组有2个元素,第二个子数组有3个元素。
	num3.Dump(); 
}

1.2 ArrayList

  • 底层有扩容机制,所以是连续可变空间
  • 底层类型是object,涉及装箱拆箱操作,对性能有影响

代码演示:

void TestArrayList(){
	ArrayList num1 = new ArrayList(2);
	num1.Add("你发如雪,凄美了离别");
	num1.Add(1);
	num1.Dump();
}

1.3 List泛型数组

  • 泛型数组实际上是ArrayList一种泛型的表示方法,在实际开发中会大量应用
  • 容量长度不确定,有灵活性也避免了装箱拆箱操作
void TestList(){
	var num1 = new List<int>();
	num1.Add(1);
	num1.Add(2);
	num1.Add(3);
	num1.Add(4);
	num1.Dump();
	
	if(num1.Contains(2)){
		"数组包含2".Dump();
	}
}

1.4 HashTable

  • 无序排列
  • 使用Object类型
  • 键值对类型,key value
void TestHashTable()
{
	var num1 = new Hashtable();
	num1.Add(1, "给你的爱一直很安静");
	num1.Add("2", "来交换你偶尔给的关心");
	num1.Dump();
	
	if(num1.ContainsKey("2")){	
		"为了你的承诺".Dump();
	}
	if (num1.ContainsValue("给你的爱一直很安静"))
	{
		"在最绝望的时候".Dump();
	}
	if (!num1.ContainsValue("漂洋过海来看你"))
	{
		"都忍住不哭泣".Dump();
	}
}

1.5 Dictionary(字典)

  • Dictionary是泛型类的hashtable
  • 无序排列
  • 使用泛型,所以不涉及装拆箱操作
void TestDictionary()
{
	var num1 = new Dictionary<int, string>();
	num1.Add(1, "不管将会面对什么样的结局");
	num1.Add(2, "在漫天风沙里,望着你背影");
	num1.Add(3, "我竟悲伤的不能自己");
	
	num1.Dump();
}

1.6 Queue(队列)

  • 遵循先进先出原则

  • 支持泛型

  • 动态调整空间大小

void TestQueue(){
	var num1 = new Queue<int>(); //这里不加泛型,默认就是obeject
	num1.Enqueue(1);	//添加元素
	num1.Enqueue(2);
	num1.Enqueue(3);
	num1.Enqueue(4);
	num1.Enqueue(5);
	
	num1.Dump();
	
	num1.Dequeue();	//移除元素
	num1.Dump();
}

1.7 Stack(栈)

  • 遵循先进后出原则
  • 支持泛型
  • 动态调整空间大小
void TestStack(){
	var num1 = new Stack<int>(); //同队列一样,不加泛型默认object类型
	num1.Push(1);	//添加元素
	num1.Push(2);
	num1.Push(3);
	num1.Push(4);
	num1.Push(5);
	
	num1.Dump();
	
	num1.Pop().Dump();	//打印移除的元素
	num1.Dump();
}

2.LinQ语法

  • 方便操作集合的语法糖
  • 一般会使用到=>匿名写法

2.1 where

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 源码如上,最后返回泛型集合

  • 委托条件perdicate决定接下来执行的代码(这里不截全)

  • 根据查询条件拿取对应元素最后返回IEnumerable可枚举类型的变量

void Main()
{
	var personList = new List<Person>();
	personList.Add(new Person()
	{
		ID = 1,
		Name = "张三",
	});
	personList.Add(new Person()
	{
		ID = 2,
		Name = "张四",
	});
	personList.Add(new Person()
	{
		ID = 3,
		Name = "张五",
	});
	
	personList.Where(l => l.ID > 2).Dump();	//l是数组提出的元素ID大于2就输出对应元素
}

// You can define other methods, fields, classes and namespaces here
class Person{
	public int ID { get; set; }
	public string Name { get; set; }
}

2.2 OfType

  • 筛选自定类型的元素
void Main()
{
	var PersonList = new object[] { 1, 2, 3, 4, 5, "Music", "后会无期" };
	PersonList.OfType<int>().Dump();
	PersonList.OfType<string>().Dump();
}

2.3 Skip、Take、SkipLast、TakeLast

  • Skip是跳过第几位并选取后面的所有元素
  • Take是挑选几位及之前的元素
  • SkipLast是从末尾开始跳过,选取之前的所有元素
  • TakeLast是从末尾开始挑选,选取之后的所有元素
void Main()
{
	var PersonList = new object[] { 1, 2, 3, 4, 5, "Music", "后会无期" };
	PersonList.Skip(3).Dump(); //输出4后面的所有元素,不包含3
	PersonList.Take(3).Dump(); //输出3前面的所有元素,包含3
	PersonList.SkipLast(3).Dump(); //输出5前面的所有元素,不包含5
	PersonList.TakeLast(3).Dump(); //输出5后面的所有元素,包含5
}

2.4 SkipWhile、TakeWhile

  • SkipWhile只检查开头连续的元素,遇到第一个不满足条件的就停止检查,然后取后面所有元素
  • TakeWhile是从集合开头一直取元素,直到遇到第一个不满足元素的数
void Main()
{
	var PersonList = new int[] { 1, 2, 3, 4, 5, 4, 3, 2, 1};
	PersonList.SkipWhile(c => c < 3).Dump(); //包含3
	PersonList.TakeWhile(c => c < 4).Take(1).Dump(); //不包含4
}

2.5 Select

  • select作用是映射,映射怎么理解呢,简单来说把集合里的每个元素,按照写的规则,一对一地转换成另一个样子
  • select也算变相改变了元素,如第二个代码块演示
void Main()
{
	var PersonList = new int[] { 1, 2, 3, 4, 5, 4, 3, 2, 1 };
	PersonList.Select(p => $"我被拼接了 {p}").Dump();
	PersonList.Select((p, index) => $"我被拼接了 {index}").Dump(); //重载映射元素所在数组的序号
}
void Main()
{
	var personList = new List<Person>();
	personList.Add(new Person()
	{
		ID = 1,
		Name = "张三",
	});
	personList.Add(new Person()
	{ 
		ID = 2,
		Name = "张四",
	});
	personList.Add(new Person()
	{
		ID = 3,
		Name = "张五",
	});

	personList.Select(p => {
		p.ID+=1;	//所有ID+1
		return p;
	}).Dump();
}

class Person
{
	public int ID { get; set; }
	public string Name { get; set; }
}

2.6 SelectMany

  • 一句话:把"嵌套集合"拍平成一维集合
void Main()
{
	IEnumerable<List<int>> list = new List<List<int>>
	{
		new List<int>{1, 2, 3},
		new List<int>{4, 5, 6},
	};
	list.SelectMany(l => l.Select(i => $"拼接{i}")).Dump();
}

2.7 Cast、Chunk

  • Cast为转换
  • Chunk为分组
void Main()
{
	IEnumerable<object> list = new object[5]{1, 2, 3, 4, 5};
	list.Cast<int>().Dump();	//转换为int数组
	list.Chunk(2).Dump();	//两两分组,5单独为一组
}

2.8 Any、All、Contains

  • Any检测数组是否有元素符合条件
  • All检测数组所有元素都符合条件
  • Contains检测数组是否包含某个元素
void Main()
{
	IEnumerable<int> list = new int[5] { 1, 2, 3, 4, 5 };
	list.Any(l => l > 1).Dump();	//输出True
	list.All(l => l > 1).Dump();	//输出False
	list.Contains(1).Dump();	//输出True
}

2.9 Append、Prepend

  • Append是往数组开头添加元素
  • Prepend是往数组结尾添加元素
void Main()
{
	IEnumerable<int> list = new int[5] { 1, 2, 3, 4, 5 };
	list.Append(0).Dump();	//数组开头添加0
	list.Prepend(7).Dump();	//数组结尾添加7
}

2.10 Count、TryGetNonEnumeratedCount、LongCount

  • Count统计满足条件的元素数量,LongCount就是返回long类型的Count方法
  • Count使用时会用foreach遍历数组,在面对庞大数据时会有灾难级的性能损耗
  • TryGetNonEnumeratedCount不遍历集合就能尝试获取元素数量,避免性能损耗
void Main()
{
	IEnumerable<int> list = new int[5] { 1, 2, 3, 4, 5 };
	list.Count(l => l > 3).Dump();
    list.TryGetNonEnumeratedCount(out int result);
	result.Dump();	//输出5
}

2.11 Max、Min、MaxBy、MinBy

  • Max和Min分别是求最大值和最小值
  • 要注意的是,Max和Min求的结果是相对于这个数组而言的,什么意思呢?看代码演示就知道了
  • MaxBy和MinBy是根据变换值返回原始对象
void Main()
{
	IEnumerable<int> list = new int[5] { 1, 2, 3, 4, 5 };
	list.Max().Dump();	//输出5
	list.Min().Dump();	//输出1
	list.Max(l => l* -1).Dump();	//输出-1,这就是为什么结果是相对数组而言的,操作原数字-5就不是最大的了
	list.Min(l => l* -1).Dump();	//输出-5
    list.MinBy(l => l* -1).Dump();	//输出5
	list.MaxBy(l => l* -1).Dump();	//输出1
}

2.12 Aggregate

  • Aggregate意思为聚合,单纯用来添加的话用法则非常像Sum
  • 可以添加初始值,也可以对结果进行改变
void Main()
{
	IEnumerable<int> list = new int[5] { 1, 2, 3, 4, 5 };
	list.Aggregate((x, y) => x + y).Dump(); //输出15
	list.Aggregate(10, (x, y) => x + y).Dump(); //10为初始值,输出25
	list.Aggregate(10, (x, y) => x + y, x => x / 5).Dump(); //结果/5,输出5
}

2.13 First、Single

  • First返回数组的第一个元素,添加条件后则返回满足条件的第一个元素
  • Single是保证数组只有一个元素,并返回它
  • First和Single尽量要捕获异常,因为数组为空等情况都会终止程序运行
void Main()
{
	IEnumerable<int> list = new int[5] { 1, 2, 3, 4, 5 };
	list.First().Dump();	//输出1
	
	IEnumerable<int> list2 = new int[] {};
	list2.FirstOrDefault().Dump();	//输出0,因为int默认值为0
	
	IEnumerable<int> list3 = new int[1] {1};
	list3.Single().Dump();	//输出1
	list3.SingleOrDefault().Dump();	//输出1,这里不多举例,知道默认值即可
}

2.14 ElementAt、ElementAtOrDefault

  • 二者都是根据索引返回相应元素,后者仍是返回默认值
  • ElementAt和First等一样,最好写异常捕获
void Main()
{
	IEnumerable<int> list = new int[5] { 1, 2, 3, 4, 5 };
	list.ElementAt(3).Dump();	//输出4
}

2.15 DefaultIfEmpty

  • 处理空集合时,提供一个默认值代替空集合
void Main()
{
	var personList = new List<Person>();	//集合为空
	personList.DefaultIfEmpty(new Person {
		ID = 4,
		Name = "不管将会面对什么样的结局"
	}).First().Dump();
}

class Person
{
	public int ID { get; set; }
	public string Name { get; set; }
}

2.16 ToArray、ToList、ToDictionary、ToHashTable

  • 都是一系列的转换,这里只演示ToDictionary
void Main()
{
	IEnumerable<int> list = new int[5] { 1, 2, 3, 4, 5 };
	list.ToDictionary(key=>key, value=>value.ToString()).Dump();	//注意key值不能重复
}

2.17 Distinct、DistinctBy、Union、Intersect、Except

  • Distinct:去掉集合中的重复元素,只保留唯一的

  • DistinctBy:就是"按某个特征去重",相同特征只留第一个

void Main()
{
	IEnumerable<int> list = new int[6] { 1, 1, 2, 3, 4, 5 };
	list.Distinct().Dump(); //输出五个不同元素

	var personList = new List<Person>();
	personList.Add(new Person()
	{
		ID = 2,
		Name = "张三",
	});
	personList.Add(new Person()
	{
		ID = 2,
		Name = "张四",
	});
	personList.Add(new Person()
	{
		ID = 3,
		Name = "张五",
	});

	personList.DistinctBy(p => p.ID = 2).Dump(); //只保留一个Id为2的张三(因为位置靠前)
}

class Person
{
	public int ID { get; set; }
	public string Name { get; set; }
}

2.18 Union、Intersect、Except

  • Union为并集,两个数组元素全部包含
  • Intersect为交集,保留两个数组相同的元素
  • Except为差集,一个数组减去另一个数组含有相同的元素
void Main()
{
	IEnumerable<int> list2 = new int[3] { 1, 2, 3};
	IEnumerable<int> list3 = new int[3] { 2, 3, 4};
	
	list2.Union(list3).Dump(); //并集,输出4个元素
	list2.Intersect(list3).Dump(); //交集,输出2、3
	list2.Except(list3).Dump(); //差集,输出1
}

2.19 SequenceEqual

  • 比较两个数组是否相等
void Main()
{
	IEnumerable<int> list = new int[3] { 1, 2, 3};
	IEnumerable<int> list2 = new int[3] { 1, 2, 3};
	IEnumerable<int> list3 = new int[3] { 2, 3, 4};
	
	list.SequenceEqual(list2).Dump();	//True
	list.SequenceEqual(list3).Dump();	//False
}

2.20 Zip、Concat、GroupBy

  • Zip是组合两个数组,不同类型也可以结合
  • Concat也是组合两个数组(同类型),但是会开辟空间存放新的元素,和zip有所区别
  • GruopBy是依据条件对数组进行分组,不设条件默认按照值分组
void Main()
{
	IEnumerable<int> list = new int[3] { 1, 2, 3 };	
	IEnumerable<int> list2 = new int[3] { 1, 2, 3 };
	IEnumerable<string> list3 = new string[3] { "1", "2", "3" };
    IEnumerable<int> list4 = new int[4] { 1, 1, 2, 3 };

	list.Zip(list2, (first, second) => first + second).Dump();	//输出2、4、6
	list.Zip(list3, (first, second) => first + second).Dump();	//输出11、22、33
    list.Concat(list2).Dump(); //输出1,2,3,1,2,3
    list4.GroupBy(l => l).Dump();	//分为3组,两个1为一组剩下各为两组
    list4.GroupBy(l => l > 1).Dump(); //分为2组,两个1为一组剩下两个大于1的为一组
}

2.21 OrderBy、OrderByDescending、ThenBy、ThenByDescending

  • OrderBy就是依据条件对数组的元素进行排序,后面接Descending则是倒序
  • ThenBy为后续的附加条件
void Main()
{
	var students = new[]{
		new {ID = 5, Name = "甲", Age = 22},
		new {ID = 4, Name = "乙", Age = 13},
		new {ID = 3, Name = "丙", Age = 23}	,
		new {ID = 2, Name = "丁", Age = 15}	,
		new {ID = 1, Name = "戊", Age = 42}	,
	};
	
	students.OrderBy(s => s.ID).Dump(); //按照ID从小到大排序 	
	students.OrderByDescending(s => s.Age).Dump(); //按照年龄倒序排序
	students.OrderBy(s => s.Age).ThenByDescending(s => s.Name).Dump(); //ThenBy为额外附加条件
}

3.Parallel LINQ

  • 让LINQ查询并行执行,自动利用多核CPU加速处理

  • 这章真不行了,讲的很粗糙,目前了解即可

  • Parallel是线程安全的

  • Parallel方法总结:

    • AsParallel:使用并行

    • WithDegreeOfParallelism:控制核心运行数量,设置电脑并行的CPU数

    • WithExecutionMode:强制切换多线程

    • WithMergeOptions:控制并行计算的结果如何返回给你,有三种合并模式

      • NotBuffered:不缓冲,直接返回结果
      • AutoBuffered(默认):自动缓冲,由机器决定
      • FullyBuffered:完全缓冲,等所有计算结果计算完一次性返回
    • WithCancellation:等多线程后这个会仔细说,简单说给PLINQ查询加个"停止按钮",可以随时取消长时间运行的操作

    • AsOrdered:控制并行计算的结果顺序,保持原顺序

    • AsUnOrdered(默认):顺序不重要,怎么快怎么来

    • AsSequential:在并行查询中临时切换回顺序执行

    • ParallelEnumerable:和普通集合类似功能,有三种将输入元素指派到线程的划分策略

      • Range :直接生成并行范围的数字序列
      • Repeat :生成重复元素的并行序列
      • Empty:创建空的并行序列
    • ForAll:并行地对集合中每个元素执行一个操作,不收集结果

void Main()
{
    TestAsParallelNotUse();  // 测试顺序执行
    TestAsParallel();        // 测试并行执行
}

void TestAsParallelNotUse()
{
    var stopwatch = Stopwatch.StartNew();	//Stopwatch是监管方法执行时间
    var collection = Enumerable.Range(0, 10).Select(HeavyComputation);	//执行0-10的运算
    
    // 这里才开始真正计算(因为LINQ延迟执行)
    foreach (var element in collection)
    {
        // 空的,只是触发计算
    }
    
    stopwatch.Stop();
    stopwatch.ElapsedMilliseconds.Dump("顺序执行时间");
}

void TestAsParallel()
{
    var stopwatch = Stopwatch.StartNew();
    var collection = Enumerable.Range(0, 10)
                              .AsParallel()  // 关键区别,使用并行
                              .Select(HeavyComputation);
    
    foreach (var element in collection)
	{
		// 空的,只是触发计算
	}

	stopwatch.Stop();
	stopwatch.ElapsedMilliseconds.Dump("并行执行时间");
}

int HeavyComputation(int n)
{
	long sum = n;
	for (int i = 0; i < 100_000_000; i++)
	{
		sum += i;
	}
	return n;
}
Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐