C# 集合

雨水的命运 / 2023-05-19 / 原文

简介

集合表示一组具有某种性质的数学元素,引用到程序设计中表示一组具有相同性质的对象。集合的大小可以动态调整,也可以在运行时添加或删除元素

官方文档

  • System.Collections
  • 数组
  • 集合
  • 泛型集合
  • Concurrent线程安全集合
  • System.Collections.Specialized

集合基类接口

  • ICollection
  • IEnumerable
  • IQueryable

集合列表

  • List
  • ArrayList
  • Dictionary
  • Hashtable
  • Hashset
  • Queue
  • Stack
  • SortedList
  • SortedDictionary
  • LinkedList
  • BitArray
  • ListDictionary
  • HybridDictionary
  • Channels
  • PriorityQueue
  • BlockingCollection

常用集合

数组

int[] arr = { 1, 2 };
arr.Append(3);//添加
Array.Find(arr,i => i == 1);//查找某个元素
arr= Array.FindAll(arr, i => i != 1).ToArray();//删除
Console.WriteLine(String.Join(",", arr));//以逗号间隔输出数组

ArrayList

ArrayList list = new ArrayList();
list.Add(10);//单个添加
foreach (int number in new int[5] { 1, 2, 3, 4, 5 })
{
    list.Add(number);//循环添加一个数组
}
int[] number2 = new int[2] { 11, 12 };
list.AddRange(number2);//集体添加
list.Remove(3);//删除值为3的
list.RemoveAt(3);//移除下标为3的
ArrayList list2 = new ArrayList(list.GetRange(1, 3));//新ArrayList(list2)只取旧ArrayList(list)中的一部份List<T>.GetRange(Index, Count)从下标为1的开始取三个
Console.WriteLine("Method One:");
foreach (int i in list)
{
    Console.WriteLine(i);//遍历方法一
}
Console.WriteLine("Method Two:");
for (int i = 0; i != list2.Count; i++)//数组是length
{
    int number = (int)list2[i];//一定要强制转换
    Console.WriteLine(number);//遍历方法二
}

BitArray

 BitArray 类管理一个紧凑型的位值数组,它使用布尔值来表示,其中 true 表示位是开启的(1),false 表示位是关闭的(0)。当您需要存储位,但是事先不知道位数时,则使用点阵列。您可以使用整型索引从点阵列集合中访问各项,索引从零开始。
 

 // 创建两个大小为 8 的点阵列
BitArray ba1 = new BitArray(8);
BitArray ba2 = new BitArray(8);
byte[] a = { 60 };
byte[] b = { 13 };
// 把值 60 和 13 存储到点阵列中
ba1 = new BitArray(a);
ba2 = new BitArray(b);
// ba1 的内容
Console.WriteLine("Bit array ba1: 60");
for (int i = 0; i < ba1.Count; i++)
{
    Console.Write("{0, -6} ", ba1[i]);
}
Console.WriteLine();

// ba2 的内容
Console.WriteLine("Bit array ba2: 13");
for (int i = 0; i < ba2.Count; i++)
{
    Console.Write("{0, -6} ", ba2[i]);
}
Console.WriteLine();


BitArray ba3 = new BitArray(8);
ba3 = ba1.And(ba2);

// ba3 的内容
Console.WriteLine("Bit array ba3 after AND operation: 12");
for (int i = 0; i < ba3.Count; i++)
{
    Console.Write("{0, -6} ", ba3[i]);
}
Console.WriteLine();

ba3 = ba1.Or(ba2);
// ba3 的内容
Console.WriteLine("Bit array ba3 after OR operation: 61");
for (int i = 0; i < ba3.Count; i++)
{
    Console.Write("{0, -6} ", ba3[i]);
}

List

var list = new List<string> { "a", "c" };

字典

//New方法
var dicA = new Dictionary<int, string>
{
    [1] = "a",
    [5] = "e"
};
//Old方法
var dicB = new Dictionary<int, string>
{
    {1, "a"},
    {5, "b"}
};

//判断Key是否存在
dicA.ContainsKey(1);
//根据键获取值
dicA.GetValueOrDefault(1);
string stra = string.Empty;
dicA.TryGetValue(1, out stra);

//遍历字典
foreach (KeyValuePair<int, string> kvp in dicA)
{
    Console.WriteLine(kvp.Key + kvp.Value);
}

//获取哈希表中键集合
Dictionary<int, string>.KeyCollection keyColl = dicA.Keys;

//获取哈希表值集合
Dictionary<int, string>.ValueCollection valueColl = dicA.Values;

//ToDictionary分组
string[] str = new string[] { "Car", "Bus", "Bicycle" };
var d = str.ToDictionary(item => item, item => true);
var f= str.GroupBy(k=>k).ToDictionary(k => k.Key, v => v.Count());

Hashtable

Hashtable 遍历是无序的

Hashtable ht = new Hashtable();

ht.Add("1", "hello");
ht.Add("2", "world");
ht.Add("3", "I");
ht.Add("4", "Love");
ht.Add("5", "China");

//遍历方法一:遍历哈希表中的键
foreach (string key in ht.Keys)
{
    //Console.WriteLine(string.Format("{0}-{1}"), key, ht[key]);
    Console.WriteLine(string.Format("{0}-{1}", key, ht[key]));
}
Console.WriteLine("**********************************************************");
//遍历方法二:遍历哈希表中的值
foreach (string value in ht.Values)
{
    Console.WriteLine(value);
}
Console.WriteLine("**********************************************************");
//遍历方法三:遍历哈希表中的键值
foreach (DictionaryEntry de in ht)
{
    Console.WriteLine(string.Format("{0}-{1}", de.Key, de.Value));
}

HashSet

int[] arr = { 1, 2, 3 };
HashSet<int> hashSet = new HashSet<int>(arr);
hashSet.Add(4);
foreach (var city in hashSet)
{
    Console.WriteLine(city);
}

SortedList

SortedList sl = new SortedList();

sl.Add("1", "hello");
sl.Add("2", "world");
sl.Add("3", "I");
sl.Add("4", "Love");

foreach (var item in sl.Keys)
{
    Console.WriteLine(item + ":" + sl[item]);
}
int myIndex = 1;
Console.WriteLine("The key at index {0} is {1}.", myIndex, sl.GetKey(myIndex));//获得下标为1的键的名称
Console.WriteLine("The value at index {0} is {1}.", myIndex, sl.GetByIndex(myIndex));//获得键为1的值

Queue

队列(Queue)代表了一个先进先出的对象集合。enqueue方法入队列,dequeue方法出队列


Queue q = new Queue();

q.Enqueue('A');
q.Enqueue('B');
q.Enqueue('C');
q.Enqueue('D');

Console.WriteLine("Current queue: ");//队列先进先出
foreach (char value in q)
    Console.Write(value + " ");
Console.WriteLine();
q.Enqueue('V');//向 Queue 的末尾添加一个对象。
q.Enqueue('H');
Console.WriteLine("Current queue: ");
foreach (char value in q)
    Console.Write(value + " ");
Console.WriteLine();
Console.WriteLine("Removing some values ");
char ch = (char)q.Dequeue();//移除并返回在 Queue 的开头的对象。
Console.WriteLine("The removed value: {0}", ch);
ch = (char)q.Dequeue();
Console.WriteLine("The removed value: {0}", ch);

Stack

堆栈(Stack)代表了一个后进先出的对象集合。Push方法入栈,Pop方法出栈。

Stack st = new Stack();

st.Push('A');
st.Push('B');
st.Push('C');
st.Push('D');

Console.WriteLine("Current stack: ");//当前队列,先存后出
foreach (char value in st)
{
    Console.Write(value + " ");
}
Console.WriteLine();
st.Push('V');//向 Stack 的顶部添加一个对象。
st.Push('H');
Console.WriteLine("The next poppable value in stack: {0}", st.Peek());//返回在 Stack 的顶部的对象,但不移除它。
Console.WriteLine("Current stack: ");
foreach (char value in st)
{
    Console.Write(value + " ");
}
Console.WriteLine();
Console.WriteLine("Removing values ");
st.Pop();//移除并返回在 Stack 的顶部的对象
st.Pop();
st.Pop();

Console.WriteLine("Current stack: ");
foreach (char value in st)
{
    Console.Write(value + " ");
}

PriorityQueue

优先级出队

PriorityQueue<string, int> priorityQueue = new();

priorityQueue.Enqueue("Second", 2);
priorityQueue.Enqueue("Fourth", 4);
priorityQueue.Enqueue("Third 1", 3);
priorityQueue.Enqueue("Third 2", 3);
priorityQueue.Enqueue("First", 1);

while (priorityQueue.Count > 0)
{
    string item = priorityQueue.Dequeue();
    Console.WriteLine(item);
}

// Output:
// First
// Second
// Third 2
// Third 1
// Fourth

Channel

using Microsoft.IO;
using System.Threading.Channels;

namespace ConsoleApp1;

public class Program
{

    static void Main(string[] args)
    {
        //不限容Channel
        var channel = Channel.CreateUnbounded<int>();
        //限容Channel
        var channelBounded = Channel.CreateBounded<string>(new BoundedChannelOptions(1000) { FullMode = BoundedChannelFullMode.Wait });

    }

    public static async Task SingleProducerSingleConsumer()
    {
        var channel = Channel.CreateUnbounded<int>();

        // 生产者写入消息
        channel.Writer.TryWrite(1);
        await channel.Writer.WriteAsync(2);

        //生产者也可以明确告知消费者不会发送任何消息了
        channel.Writer.Complete();

        //当有空间可写时,返回一个true。因为Channel有限容类型的Channel,所以这个方法也可以作为一个屏障,当Channel空间已满时,进行阻塞
        if (await channel.Writer.WaitToWriteAsync()) { }


        //async stream,在没有被生产者明确Complete的情况下,这里会一致阻塞下去
        await foreach (var item in channel.Reader.ReadAllAsync())
        {
            Console.WriteLine(item);
        }
    }

    public static async Task SingleProducerSingleConsumer2()
    {
        var channel = Channel.CreateUnbounded<int>();

        Task.Run(async () =>
        {
            for (int i = 0; i < 10; i++)
            {
                await channel.Writer.WriteAsync(i);
                if (i > 5)
                {
                    channel.Writer.Complete();
                }
            }
        });

        Task.Run(async () =>
        {
            await foreach (var item in channel.Reader.ReadAllAsync())
            {
                Console.WriteLine(item);
            }
        });
    }

    public static async Task SingleProducerSingleConsumer3()
    {
        var channel = Channel.CreateUnbounded<int>();
        var reader = channel.Reader;
        for (int i = 0; i < 10; i++)
        {
            await channel.Writer.WriteAsync(i + 1);
        }

        while (await reader.WaitToReadAsync())
        {
            //var i2= await reader.ReadAsync();
            if (reader.TryRead(out var i))
            {
                Console.WriteLine(i);
            }
        }
    }
}

BlockingCollection

using System.Collections.Concurrent;

BlockingCollection<string> bc = new();

//先进先出(FIFO)
BlockingCollection<string> bc2 = new(new ConcurrentQueue<string>());

//先进后出(LIFO)
BlockingCollection<int> bc3 = new(new ConcurrentStack<int>());

//生产者
Task.Factory.StartNew(async () =>
{
    int count = 0;
    while (true)
    {
        bc.Add("String: " + count);
        count++;
        await Task.Delay(1000 * 5);

        //if (count > 10)
        //{
        //    bc.CompleteAdding();  //代表添加完成
        //}
    }
});

//消费者
Task.Factory.StartNew(async () =>
{
    while (true)
    {
        foreach (var element in bc.GetConsumingEnumerable())
        {
            Console.WriteLine("Work: " + element);
        }
        await Task.Delay(1000 * 5);
    }
});

元组

元组不是集合

官方文档

var user = ("小红", 18);
Console.WriteLine($"Name:{user.Item1}, Age:{user.Item2}");
//ValueTuple是值类型,Tuple是引用类型
Tuple<string, int> tuple = new Tuple<string, int>("小红", 18);
ValueTuple<string, int> valueTuple = new ValueTuple<string, int>("小红", 18);

集合常用方法

数组关联查询

int[] arrA = { 1, 2, 3 };
int[] arrB = { 2, 4 };
var Result1 = arrA.Union(arrB); //1234
var Result2 = arrA.Concat(arrB); //12324
var Result3 = arrA.Intersect(arrB); //2
var Result4 = arrA.Except(arrB); //13
var Result5 = arrA.Except(arrB).Union(arrB.Except(arrA));//134

Take/Skip

int[] arr = { 0, 1, 2, 3, 4, 5 };
Console.WriteLine(string.Join(',',arr.Take(2))); //0,1
Console.WriteLine(string.Join(',', arr.Skip(2))); //2,3,4,5
Console.WriteLine(string.Join(',', arr.TakeLast(2))); //4,5
Console.WriteLine(string.Join(',', arr.SkipLast(2))); //0,1,2,3

var source= Enumerable.Range(1, 10);
source.ElementAt(^2); // returns 9
var xx = source.Take(7).Skip(2).ToList(); // returns 34567
//source.Take(..3) 代替 source.Take(3)
//source.Take(3..) 代替 source.Skip(3)
//source.Take(2..7) 代替 source.Take(7).Skip(2)
//source.Take(^3..) 代替 source.TakeLast(3)
//source.Take(..^3) 代替 source.SkipLast(3)
//source.Take(^7..^3)而不是.source.TakeLast(7).SkipLast(3)

分页

var arr = Enumerable.Range(1, 100);
int pageindex = 3;
int pageSize = 5;
var items = arr.Skip((pageindex - 1) * pageSize).Take(pageSize);
foreach (var book in items)            
    Console.WriteLine(book);            

long pageCount = (long)Math.Ceiling(arr.LongCount() * 1.0 / pageSize);
Console.WriteLine("总页数" + pageCount);

EF Core分页

// <summary>
/// 分页方法
/// </summary>
/// <param name="pageindex">页码从1开始</param>
/// <param name="pageSize">每一页的数据条数</param>
static void printpage(int pageindex, int pageSize)
{
    using (MydatabaseContext mydatabase = new MydatabaseContext())
    {
        //过滤到不想要的数据
        //Contains()可以用来判断序列中是否存在指定的元素。
        //过滤掉不行要的数据
        IQueryable<TBook> books = mydatabase.TBooks.Where(a => !a.Bookname.Contains("李四"));

        //减一是因为要从零开始
        var items = books.Skip((pageindex - 1) * pageSize).Take(pageSize);
        foreach (var book in items)
        {
            Console.WriteLine(book.Bookname);
        }
        long count = books.LongCount();
        long pageCount = (long)Math.Ceiling(count * 1.0 / pageSize);
        Console.WriteLine("总页数" + pageCount);
    }
}

Enumerable常用方法

官方文档

Range/Repeat/Empty

//1,2,3...100
var a=Enumerable.Range(1,100).ToList();

//新建空对象
var b=Enumerable.Empty<int>().AsQueryable();

//添加重复数据
IEnumerable<int> ints = Enumerable.Repeat(1, 10);
//输出 1 1 1 1 1 1 1 1 1 1
foreach (int i in ints) Console.WriteLine(i);   

Append/Prepend

  • Append:向序列的尾部添加一个值。
  • Prepend:向序列的开头添加一个值。
var arr = Enumerable.Range(1, 2);
arr = arr.Append(3);
arr = arr.Prepend(4);
//4123
foreach (int i in arr) Console.WriteLine(i);

Cast/OfType

ArrayList al = new ArrayList { 1, 2, "a" };
//会抛出异常
IEnumerable<int> IEInt = al.Cast<int>();
//自动移除无法转换的元素
IEnumerable<int> IEOfType = al.OfType<int>();
//输出 1 2
foreach (int i in IEOfType) Console.WriteLine(i);

Reverse

反转集合元素

var arr = Enumerable.Range(1, 5);
arr = arr.Reverse();
//54321
foreach (int i in arr) Console.Write(i);

Chunk

切片

// { {0,1,2}, {3,4,5}, {6,7,8}, {9} }
List<int[]> list = Enumerable.Range(1, 10).Chunk(3).ToList();

ZIP

int[] numbers = { 1, 2, 3, 4 };
string[] words = { "one", "two", "three" };
var numbersAndWords = words.Zip(numbers, (first, second) => first + " " + second);
var caxz = numbersAndWords.ToList();

var xs = Enumerable.Range(1, 10);
var ys = xs.Select(x => x.ToString());
var zs = xs.Select(x => x % 2 == 0);
foreach ((int x, string y, bool z) in Enumerable.Zip(xs, ys, zs)) { }