Array与collection

134 阅读4分钟

集合

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);//求两个集合的对称差集。