CH04-集合
CH04-集合
本章目标
- 理解集合的概念
- 会使用集合初始化器
- 熟练使用集合访问数据理
- 解泛型的概念
- 熟练使用各种泛型集合
本章内容
1、理解集合的概念
为什么要使用集合?
回顾:当需要保存多个相同类型数据的时候,之前使用的是数组,那么数组有什么特点呢?
- 存储数据个数固定,当需要在数组中添加新元素时,则需要重新添加长度更大的突间,从数组中间删除数据的时候,则需要大量做数据移动,不然浪费空间。
- 有没有更好的方式解决以上问题呢?集合
什么是集合?
集合(Collection)类是专门用于数据存储和检索的类。
2、动态数组集合:ArrayList
概念:动态数组(ArrayList)代表了可被单独索引的对象的有序集合。它基本上可以替代一个数组。但是,与数组不同的是,您可以使用索引在指定的位置添加和移除项目,动态数组会自动重新调整它的大小。它也允许在列表中进行动态内存分配、增加、搜索、排序各项。
ArrayList常用属性和方法:
常用属性:
属性 描述 Capacity 获取或设置 ArrayList 可以包含的元素个数。 Count 获取 ArrayList 中实际包含的元素个数。 常用方法:
序号 方法名 & 描述 1 public virtual int Add( object value ); 在 ArrayList 的末尾添加一个对象。 2 public virtual void AddRange( ICollection c ); 在 ArrayList 的末尾添加 ICollection 的元素。 3 public virtual void Clear(); 从 ArrayList 中移除所有的元素。 4 public virtual bool Contains( object item ); 判断某个元素是否在 ArrayList 中。 5 public virtual ArrayList GetRange( int index, int count ); 返回一个 ArrayList,表示源 ArrayList 中元素的子集。 6 public virtual int IndexOf(object); 返回某个值在 ArrayList 中第一次出现的索引,索引从零开始。 7 public virtual void Insert( int index, object value ); 在 ArrayList 的指定索引处,插入一个元素。 8 public virtual void InsertRange( int index, ICollection c ); 在 ArrayList 的指定索引处,插入某个集合的元素。 9 public virtual void Remove( object obj ); 从 ArrayList 中移除第一次出现的指定对象。 10 public virtual void RemoveAt( int index ); 移除 ArrayList 的指定索引处的元素。 11 public virtual void RemoveRange( int index, int count ); 从 ArrayList 中移除某个范围的元素。 12 public virtual void Reverse(); 逆转 ArrayList 中元素的顺序。 13 public virtual void SetRange( int index, ICollection c ); 复制某个集合的元素到 ArrayList 中某个范围的元素上。 14 public virtual void Sort(); 对 ArrayList 中的元素进行排序。 15 public virtual void TrimToSize(); 设置容量为 ArrayList 中元素的实际个数。 ArrayList的使用步骤
引入命名空间:System.Collections
1
using System.Collections;
创建ArrayList对象:
1
2ArrayList engineers = new ArrayList();
ArrayList engineers = new ArrayList(5)//指定集合容量为5使用具体属性和方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
//1.在集合中添加元素,Add方法是添加的object类型
//所以会进行装箱操作
ArrayList arrayList = new ArrayList(4);
Console.WriteLine("容量:{0}", arrayList.Capacity);
//1.调用添加的方法,Add方法参数为object类型,所以将进行装箱操作
arrayList.Add("张三");
arrayList.Add("李四");
arrayList.Add("王五");
arrayList.Add("张小丽");
arrayList.Add("李大大");
Console.WriteLine("容量:{0}", arrayList.Capacity);//输出8
Console.WriteLine("集合中元素数量:{0}",arrayList.Count);//输出5
//2.遍历集合里的元素
Console.WriteLine("foreach遍历:");
foreach (var item in arrayList)
{
Console.Write(item+" ");
}
Console.WriteLine("\nfor遍历:");
for (int i = 0; i < arrayList.Count; i++)
{
Console.Write(arrayList[i]+" ");
}
//3、常用方法
arrayList.Remove("王五");//移除对象
arrayList.RemoveAt(0);//根据索引移除
arrayList.Reverse();//逆转 ArrayList 中元素的顺序
arrayList.AddRange(new string[] {"a","b","c" });//添加集合
Console.WriteLine("\nfor遍历:");
for (int i = 0; i < arrayList.Count; i++)
{
Console.Write(arrayList[i] + " ");
}
ArrayList集合初始化器
概念:
C#3.0语言的新特性之一由一系列元素初始化器构成,包围在“{”和“}”之间,并使用逗号进行分隔
作用:
对集合数据作初始化操作
语法:
1
ArrayList arrayList2 = new ArrayList() {"张三","李四","王五" };
通过对象演示集合初始化器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33static void Main(string[] args)
{
Student stu1 = new Student();
stu1.StudentName = "张三";
stu1.Gender = Gender.男;
Student stu2 = new Student() { StudentName = "李四", Gender = Gender.男 };
Student stu3 = new Student() { StudentName = "小莉", Gender = Gender.女 };
//通过集合初始化器,将Student对象添加到集合中
ArrayList arrayList = new ArrayList() { stu1, stu2, stu3 };
//循环遍历
foreach (var item in arrayList)
{
Student stu = (Student)item;//拆箱操作,将object对象转换成Student对象
stu.SayHi();
}
}
public enum Gender
{
男, 女
}
public class Student
{
public string StudentName { get; set; }
public Gender Gender { get; set; }//定义性别的枚举
public void SayHi()
{
Console.WriteLine("姓名:{0},性别:{1}", StudentName, Gender);
}
}
3、哈希表:HashTable
为什么要使用HashTable?
ArrayList是通过索引方式获取数据,当索引发生变化,要精确获取到对应数据就比较麻烦,能否有一种方式不管索引怎么发生变化,都可以通过名称或键找到对应的数据呢?
HashTable则可以解决此类型问题
根据键(Key)可以查找到相应的值 (Value)
什么是HashTable?
Hashtable 类代表了一系列基于键的哈希代码组织起来的键/值对。
它使用键来访问集合中的元素。
当您使用键访问元素时,则使用哈希表,而且您可以识别一个有用的键值。
哈希表中的每一项都有一个键/值对。
键用于访问集合中的项目。
HashTable的属性和方法:
常用属性:
属性 描述 Count 获取 Hashtable 中包含的键值对个数。 IsFixedSize 获取一个值,表示 Hashtable 是否具有固定大小。 IsReadOnly 获取一个值,表示 Hashtable 是否只读。 Item 获取或设置与指定的键相关的值。 Keys 获取一个 ICollection,包含 Hashtable 中的键。 Values 获取一个 ICollection,包含 Hashtable 中的值。 常用方法:
序号 方法名 & 描述 1 public virtual void Add( object key, object value ); 向 Hashtable 添加一个带有指定的键和值的元素。 2 public virtual void Clear(); 从 Hashtable 中移除所有的元素。 3 public virtual bool ContainsKey( object key ); 判断 Hashtable 是否包含指定的键。 4 public virtual bool ContainsValue( object value ); 判断 Hashtable 是否包含指定的值。 5 public virtual void Remove( object key ); 从 Hashtable 中移除带有指定的键的元素。 使用HashTable:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22//1、创建对象
Hashtable hashtable = new Hashtable();
//2、通过键值对,添加对象
hashtable.Add("zs", "张三");
hashtable.Add("ls", "李四");
hashtable.Add("xl", "小利");
//3、获取对象,必须通过键获取,而且要将object转换成具体对象
string name = (string)hashtable["ls"];
//4、循环遍历数据,可通过HashTable的键的集合
foreach (object key in hashtable.Keys)
{
Console.WriteLine("键:{0},值:{1}", key, hashtable[key]);
}
//5、可直接获取值的集合
foreach (object value in hashtable.Values)
{
Console.WriteLine(value);
}
//6、根据键移除对象
hashtable.Remove("zs");
hashtable.Clear();//清空集合
4、List泛型集合
为什么要使用泛型?
使用集合存储数据时容易出现的问题:
(1)对象存储不易控制,添加时为object对象
(2)类型转换容易出错,会将object对象转换成具体对象,因为添加时未进行类型检查,则容易出错
什么是泛型集合?
概念:
泛型
最常见的用途是创建集合类 泛型集合可以约束集合内的元素类型
典型泛型集合List
、Dictionary<K,V> 、<K,V>表示该泛型集合中的元素类型
使用List
泛型集合: 命名空间:System.Collections.Generic(通常情况下,创建类文件时默认此命名空间)
创建List
对象: 1
List<类型> list=new List<类型>();
演示案例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14//创建string的泛型集合
List<string> list = new List<string>();
//添加操作时,必须为string类型,约束了相应类型
list.Add("张三");
list.Add("李四");
string name = list[0];//通过索引获取,不需要类型转换
//遍历数据时,直接可用string类型接受,不需类型转换
foreach (string str in list)
{
Console.WriteLine(str);
}List
的访问方式与ArrayList类似,可对比学习
5、Dictionary<K,V>泛型集合
概念:
Dictionary<K,V>通常称为字典<K,V>
约束集合中元素类型
编译时检查类型约束,无需装箱拆箱操作
与Hashtable操作类似。
演示案例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33//创建对象,指定键值对都为String类型
Dictionary<string ,string > dic=new Dictionary<string ,string>();
//添加元素
dic.Add("zs", "张三");
dic.Add("ls", "李四");
//根据键获取值,不需要类型转换
string val = dic["ls"];
//遍历数据
foreach (string key in dic.Keys)
{
Console.WriteLine("key:{0},value:{1}", key, dic[key]);
}
foreach (string value in dic.Values)
{
Console.WriteLine("value:{0}",value);
}
foreach (var item in dic)
{
Console.WriteLine(item.Key+":"+item.Value);
}
//初始化器
Dictionary<int, string> dics = new Dictionary<int, string>()
{
{1,"星期一"},{2 ,"星期二"},{3,"星期三"}
};
Dictionary<string, Student> stus = new Dictionary<string, Student>
{
{"zs",new Student{StudentName="张三",Gender=Gender.男 } },
{"ls",new Student{StudentName="李四",Gender=Gender.男 } },
{"xl",new Student{StudentName="小莉",Gender=Gender.女 } }
};访问 Dictionary<K,V> 与 Hashtable 的对比:
6、泛型方法
什么是泛型?
泛型是C#2.0推出的新语法,不是语法糖,而是2.0由框架升级提供的功能。
我们在编程程序时,经常会遇到功能非常相似的模块,只是它们处理的数据不一样。但我们没有办法,只能分别写多个方法来处理不同的数据类型。这个时候,那么问题来了,有没有一种办法,用同一个方法来处理传入不同种类型参数的办法呢?泛型的出现就是专门来解决这个问题的。
为什么使用泛型?
【案例】:在一个类中,添加方法来实现根据传入不一样类型的参数,获取相应的数据类型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27public class TestMethod
{
public static void ShowInt(int param)
{
Console.WriteLine("类名:{0},参数类型:{1},参数值:{2}", typeof(TestMethod).Name, param.GetType().Name, param);
}
public static void ShowString(string param)
{
Console.WriteLine("类名:{0},参数类型:{1},参数值:{2}", typeof(TestMethod).Name, param.GetType().Name, param);
}
public static void ShowDateTime(DateTime param)
{
Console.WriteLine("类名:{0},参数类型:{1},参数值:{2}", typeof(TestMethod).Name, param.GetType().Name, param);
}
}
static void Main(string[] args)
{
int intParam = 10;
string stringParam = "张三";
DateTime dateTimeParam = DateTime.Now;
TestMethod.ShowInt(intParam);
TestMethod.ShowString(stringParam);
TestMethod.ShowDateTime(dateTimeParam);
Console.ReadLine();
}运行结果:
问题:
从上面的结果中我们可以看出这三个方法,除了传入的参数不同外,其里面实现的功能都是一样的。在1.0版的时候,还没有泛型这个概念,那么怎么办呢。相信很多人会想到了OOP三大特性之一的继承,我们知道,C#语言中,object是所有类型的基类,将上面的代码进行以下优化:
优化后的代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19public class TestMethod
{
public static void ShowObject(object param)
{
Console.WriteLine("类名:{0},参数类型:{1},参数值:{2}", typeof(TestMethod).Name, param.GetType().Name, param);
}
}
static void Main(string[] args)
{
int intParam = 10;
string stringParam = "张三";
DateTime dateTimeParam = DateTime.Now;
TestMethod.ShowObject(intParam);
TestMethod.ShowObject(stringParam);
TestMethod.ShowObject(dateTimeParam);
Console.ReadLine();
}输出结果:
从上面的结果中我们可以看出,使用Object类型达到了我们的要求,解决了代码的可复用。可能有人会问定义的是object类型的,为什么可以传入int、string等类型呢?
原因有二:
1、object类型是一切类型的父类。
2、通过继承,子类拥有父类的一切属性和行为,任何父类出现的地方,都可以用子类来代替。
但是上面object类型的方法又会带来另外一个问题:装箱和拆箱,会损耗程序的性能。
微软在C#2.0的时候推出了泛型,可以很好的解决上面的问题。
通过泛型,优化代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15public static void Show<T>(T param)
{
Console.WriteLine("类名:{0},参数类型:{1},参数值:{2}", typeof(TestMethod).Name, param.GetType().Name, param);
}
static void Main(string[] args)
{
int intParam = 10;
string stringParam = "张三";
DateTime dateTimeParam = DateTime.Now;
TestMethod.Show<int>(intParam);
TestMethod.Show<string>(stringParam);
TestMethod.Show<DateTime>(dateTimeParam)
Console.ReadLine();
}运行如果:
7、泛型类
语法:
1
2
3
4
5
6
7
8
9public class 类名<T>{
//可以指定泛型属性
public T _T;
//可以指定方法类型
public T TestMethod(T param){
......
}
}演示案例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17public class TestGeneric<T>
{
public T MyProperty { get; set; }
public void TestMethod(T obj)
{
Console.WriteLine("属性值:{0},传入参数类型:{1}", MyProperty, obj.GetType().Name);
}
}
public class Test{
void Main(){
TestGeneric<int> testInt = new TestGeneric<int>();
testInt.MyProperty = 100;
testInt.TestMethod(20);
Console.ReadLine();
}
}输出结果 :
8、泛型总结优点:
- 实现代码重用,未来的主流技术
- 性能高,避免繁琐的装箱拆箱
- 提供了更好的类型安全性
- CLR支持泛型
本章总结
集合的概念是什么?
List
泛型集合常用的方法 Dictionary<TKey,TValue>泛型常用的方法
本章作业
实现考勤信息管理,具体说明:使用集合保存数据
需求说明:
1、实现员工信息和考勤管理系统
- 实现新增员工(员工ID唯一)
- 使用DataGridView控件展示员工列表
2、使用DataGridView展示集合的数据,参考代码如下:
1
this.dgvProgrammer.DataSource = new BindingList<SE>(list);
员工信息查询和删除:
需求说明:
- 实现根据员工工号进行模糊查询
- 实现删除员工信息
实现员工的签到和签退功能
需求说明:
实现员工签到和签退
- 每天只能签到1次
- 签退前必须已经签到
实现思路:
编写考勤记录类
- RecordRecord属性:员工ID、员工姓名、签到时间、签退时间
- 使用Dictionary<string,Record>保存考勤记录
- 编写签到和签退响应,并给出相应提示
显示员工考勤记录
需求说明:
使用DataGridView控件显示员工考勤记录
提示:DataGridView显示字典泛型集合数据
1
2
3BindingSource bs = new BindingSource(); //创建BindingSource对象
bs.DataSource = recordList.Values; //设置DataSource为字典Values
this.dgvRecords.DataSource = bs; //设置dgvRecords的DataSource