这一节我们来学习集合,什么是集合呢? 集合就如同数组,用来存储和管理一组特定类型的数据对象,除了基本的数据处理功能,集合直接提供了各种数据结构及算法的实现,如队列、链表、排序等,可以让你轻易地完成复杂的数据操作。在使用数组和集合时要先加入system.collections命名空间,它提供了支持各种类型集合的接口及类。集合本身上也是一种类型,基本上可以将其作为用来存储一组数据对象的容器,由于c#面向对象的特性,管理数据对象的集合同样被实现成为对象,而存储在集合中的数据对象则被称为集合元素。这里提到了接口这个概念,它也是面向对象编程进化的重要标准,我们在这里不做过多的讲解,先注重学习集合中的对象及其使用就可以了,下面我们来学习第一种集合:
动态数组ArrayList. ArrayList类提供了继承了IList接口。什么是继承呢? 这也是面向对象语言的重要特点之一,现在你们先把它理解为,如果一个对象继承了类或接口,那么它也具有了这个类和接口中的方法、属性,可以用这些继承的方法和属性来做相应的操作,比如:数组增加元素没有Add()方法,但是动态数组ArrayList继承了一个增加元素有Add()方法的接口,那么当它要增加元素的时候,不仅可以用索引,也可以用继承下来的Add()方法了。随着学习的深入,我会给大家再具体讲解继承的概念和使用继承的好处。那么下面让我们来看看动态数组所继承的这个接口IList它有什么特性呢?
Ilist接口 :定义了利用索引访问集合对象的方法,还继承了ICollection和IEnumerable接口,除实现了接口原有的方法成员外,其本身也定义多个专门的方法成员,例如新增、移除、在指定位置插入元素或是返回特定元素在集合中所在的位置索引,这些方法主要为集合对象提供类似数组的元素访问功能。 ILsit接口成员:add、insert、RemoveAt、Remove、contains、Clear、indexof方法,它最大的特色在于提供类似数组索引的访问机制。
ArrayList对象是较为复杂的数组。我们可以将它看为扩充了功能的数组,但ArrayList并不等同于数组,与数组相比,它以下功能和区别是:
1. 数组的容量是固定的,但ArrayList的容量可以根据需要自动扩充。 当我们修改了ArrayList的容量时,则可以自动进行内存重新分配和元素复制,比如往1号索引位插入n个元素,插入后,元素的索引依次向后n个位置排列,它是动态版本的数组类型。
2.ArrayList提供添加、插入或移除某一范围元素的方法。 但是在数组中,只能一次获取或设置一个元素的值,如利用索引赋值。
3.ArrayList只有一维,而数组可以是多维。 4.ArrayList可以存放任何数据类型的数据,而数组只能存储同种数据类型的数据。
这种可以存放任何数据类型的机制,我们称为装箱,本节课的后半部分我会讲解到,现在大家只需要记住,无论是什么数据类型,只要添加到动态数组中,都将转变为Object数据类型,所以在遍历Arraylist时,要定义一个Object类型的变量,用来接收遍历它的每个项的值。
如何声明一个动态数组呢?
ArrayList AL=new ArrayList( Capacity );//初始容量capacity也是可以不写的
原因就是即使不在初识化确定容量,容量不够的时候,会自动的按倍数作扩充。
接下来我们来看一下动态数组的常用属性
Capacity 获取或设置ArrayList可包含的元素数。
Count 获取ArrayList中实际包含的元素数。
IsReadOnly 获取一个值,该值表示ArrayList是否为只读。
Item 获取或设置指定索引处的元素。
动态数组的常用方法
增加元素-AL.Add(value);利用Add方法增加集合元素值
修改元素-AL[Index]=value;利用索引的方式修改元素的值 插入元素-AL.Insert(Index,value);将元素的值value,插入到第Index位置。 删除元素-AL.Clear(); 全部删除集合中的元素
AL.Remove(value);按照集合元素值删除元素
AL.RemoveAt(Index);按照集合的元素索引删除元素 缩减容量-AL.TrimToSize();将集合的容量减少到实际元素个数的大小
在执行删除操作后,要养成良好的缩减容量的习惯,节省内存空间,提高性能。 查找元素-除了按数组的索引查找外,还可以用AL.Contains(value);按照元素值查找集合, 如果包含便返回True,不包含时返回False。
得到类型--AL[index].GetType() 可以得到在index索引位的元素的数据类型
元素排序-- AL.Sort(); 元素反转-- AL.Reverse();
下面的例子我会给你们分部的演示出以上方法及属性,运行结果我会用图片的形式显示出来,帮助你们理解动态数组是使用。
添加元素和得到数据类型
1 ArrayList AL = new ArrayList(); 2 Console.WriteLine( " 容量: " + AL.Capacity); 3 Console.WriteLine( " 给动态数组添加元素: " ); 4 AL.Add( " Hello " ); 5 Console.WriteLine( " 容量: " + AL.Capacity); 6 AL.Add( " World " ); 7 foreach (Object obj in AL) 8 { 9 Console.Write(obj); 10 } 11 Console.WriteLine(); 12 Console.WriteLine( " 个数: " + AL.Count); 13 Console.WriteLine( " 容量: " + AL.Capacity); 14 Console.WriteLine( " 第1个元素的数据类型为: " + AL[ 0 ].GetType());
插入
1 AL.Insert( 1 , " c# " ); 2 Console.Write( " 在索引值为1的地方插入 " ); 3 foreach (Object obj in AL) 4 { Console.Write(obj); } 5 Console.WriteLine(); 6 Console.WriteLine( " 个数: " + AL.Count); 7 Console.WriteLine( " 容量: " + AL.Capacity);
结果 容量: 0
给动态数组添加元素:
容量: 4
Hello World
个数:2
容量: 4
第1个元素的数据类型为:System.String
在索引值为1的地方插入 Hello c# World
个数:3
容量: 4
请按任意键继续. . .
扩充容量
1 AL.Add( " 。 " ); 2 Console.WriteLine( " 容量。: " + AL.Capacity); 3 AL.Add( " --- " ); 4 Console.WriteLine( " 容量---: " + AL.Capacity); 5 6 foreach (Object obj in AL) 7 { Console.Write(obj); }
结果 容量: 0
给动态数组添加元素:
容量: 4
Hello World
个数:2
容量: 4
第1个元素的数据类型为:System.String
在索引值为1的地方插入 Hello c# World
个数:3
容量: 4
容量。: 4
容量---: 8
Hello c# World。---
请按任意键继续. . .
根据索引找到元素
1 Console.WriteLine( " \n3号索引的: " + AL[ 3 ]); 2 Console.WriteLine( " 集合中是否包含?: " + AL.Contains( " ? " )); 3 Console.WriteLine( " 经过之前操作后的集合元素: " ); 4 foreach (Object obj in AL) 5 { Console.Write(obj); } 6 7 Console.WriteLine( " \n个数: " + AL.Count); 8 Console.WriteLine( " 容量: " + AL.Capacity);
结果
容量: 0 给动态数组添加元素: 容量: 4 Hello World 个数:2 容量: 4 第1个元素的数据类型为:System.String 在索引值为1的地方插入 Hello c# World 个数:3 容量: 4 容量。: 4 容量---: 8 Hello c# World。--- 3号索引的:。 数组中是否包含?:False 经过之前操作后的数组元素: Hello c# World。--- 个数:5 容量: 8 请按任意键继续. . .
按值删
1 AL.Remove( " 。 " ); 2 AL.Remove( " ? " ); 3 4 Console.WriteLine( " 没有?个数只减少1个容量不变 " ); 5 foreach (Object obj in AL) 6 { Console.Write(obj); } 7 Console.WriteLine(); 8 Console.WriteLine( " 个数: " + AL.Count); 9 Console.WriteLine( " 容量: " + AL.Capacity);
结果
容量: 0 给动态数组添加元素: 容量: 4 Hello World 个数:2 容量: 4 第1个元素的数据类型为:System.String 在索引值为1的地方插入 Hello c# World 个数:3 容量: 4 容量。: 4 容量---: 8 Hello c# World。--- 3号索引的:。 数组中是否包含?:False 经过之前操作后的数组元素: Hello c# World。--- 个数:5 容量: 8 没有?个数只减少1个容量不变 Hello c# World--- 个数:4 容量: 8 请按任意键继续. . .
按照索引删
1 AL.RemoveAt( 3 ); 2 Console.WriteLine( " 移除3号索引位的元素: " ); 3 foreach (Object obj in AL) 4 { Console.Write(obj); } 5 Console.WriteLine(); 6 Console.WriteLine( " 个数: " + AL.Count); 7 Console.WriteLine( " 容量: " + AL.Capacity); 8 9
结果
容量: 0 给动态数组添加元素: 容量: 4 Hello World 个数:2 容量: 4 第1个元素的数据类型为:System.String 在索引值为1的地方插入 Hello c# World 个数:3 容量: 4 容量。: 4 容量---: 8 Hello c# World。--- 3号索引的:。 数组中是否包含?:False 经过之前操作后的数组元素: Hello c# World。--- 个数:5 容量: 8 没有?个数只减少1个容量不变 Hello c# World--- 个数:4 容量: 8 移除3号索引位的元素: Hello c# World 个数:3 容量: 8 请按任意键继续. . .
1
AL.TrimToSize();
//
缩减容量
2
Console.WriteLine(
"
实际容量:
"
+
AL.Capacity);
结果 容量: 0
给动态数组添加元素:
容量: 4
Hello World
个数:2
容量: 4
第1个元素的数据类型为:System.String
在索引值为1的地方插入 Hello c# World
个数:3
容量: 4
容量。: 4
容量---: 8
Hello c# World。---
3号索引的:。
数组中是否包含?:False
经过之前操作后的数组元素:
Hello c# World。---
个数:5
容量: 8
没有?个数只减少1个容量不变
Hello c# World---
个数:4
容量: 8
移除3号索引位的元素:
Hello c# World
个数:3
容量: 8
实际容量: 3
请按任意键继续. . .
1
AL.Clear();
2
Console.WriteLine(
"
清除全部元素后:
"
);
3
Console.WriteLine(
"
个数:
"
+
AL.Count);
4
Console.WriteLine(
"
容量:
"
+
AL.Capacity);
Title 容量: 0
给动态数组添加元素:
容量: 4
Hello World
个数:2
容量: 4
第1个元素的数据类型为:System.String
在索引值为1的地方插入 Hello c# World
个数:3
容量: 4
容量。: 4
容量---: 8
Hello c# World。---
3号索引的:。
数组中是否包含?:False
经过之前操作后的数组元素:
Hello c# World。---
个数:5
容量: 8
没有?个数只减少1个容量不变
Hello c# World---
个数:4
容量: 8
移除3号索引位的元素:
Hello c# World
个数:3
容量: 8
实际容量: 3
清除全部元素后:
个数:0
容量: 3
请按任意键继续. . .
通过以上的例子你们应该已经理解集合的方法,我们再来总结一下集合ArrayList相比数组有什么好处? 主要是它可以根据使用大小按需动态增加 ,不用受事先设置大小的控制,还有就是可以随意的添加、插入或移除某一范围元素,比数组要方便。但是它也有不足 ,ArrayList 不管对象是什么类型都会添加到集合j中 ,在编译时都是没有问题的,但是在遍历的时候,为防止集合中元素的类型不一致,所以最好使用object类型来接收遍历j的元素,如foreach(object i in j)这样就能减少错误,可能同学们会想,用object类型我们记住了,怎么就成弊端的呢?
这里我们就要学到另一个知识点,就是装箱和拆箱。 所谓装箱就是把值类型打包成object引用类型的一个实例中,也就是说在进行装箱的时候,必须分配并构造一个全新的对象。而拆箱就是指从对象中提取值类型,将object类型强制转换为原类型。
比如: ArrayList j=new ArrayList();
j.Add(123);
j.Add("123");
在添加时,ArrayList是会隐式的将整形的123 进行如下装箱操作: int i=123; object o=(object)i;也就是说存进j的元素都将变成object类型
而在使用这个整形的123时,ArrayList又会进行如下的拆箱操作: o=123; i=(int)o; 也就是将o再强制转换成原来的类型表现出去
想想这将是很大的性能消耗,需要进行大量的计算,至于怎么记住装箱拆箱,我们就把这个过程想象成现实生活中,你买了很多中水果(元素),为了方便搬运,我们把他们都放到一个大盒子(集合对象)里,但是因为有榴莲,我们又得把榴莲(值类型元素)单独包装好(装箱过程)再放到盒子里,到了家后,我们要打开盒子取出水果,在拿到榴莲时,要想见到真正的榴莲,我们就的把包装去掉(拆箱),我们马上就闻到了榴莲那独有的味道了(变回原类型)。哎!这个过程多麻烦呀!分了这么多步,在C#2.0出来后,就推出了新的技术来解决这个问题,那就是泛型,以后的章节我会讲解这个新特性。
下面我们来讲一下ArrayList向数组的互换。
将数组转换成动态数组
1 // 数组转换成动态数组 2 int [] c = { 1 , 2 }; 3 // 1.利用for循环添加到动态数组中 4 ArrayList d = new ArrayList(); 5 for ( int i = 0 ; i < c.Length; i ++ ) 6 { 7 d.Add(c[i]); 8 } 9 // 2.使用Adapter方法,将数组打包到动态数组中 10 ArrayList b = ArrayList.Adapter(c); 11 foreach ( int i in b) 12 { 13 Console.WriteLine(i); 14 }
动态数组转换成数组
1 // 动态数组转换成数组 2 ArrayList arl = new ArrayList( 4 ); 3 arl.Add(" W " ); 4 arl.Add(" h " ); 5 arl.Add(" y " ); 6 // arl.Add(1); 7 // 只能将包含同一种数据类型集合转换成这种类型的数组 8 // 如果ar1中a添加了元素整型1,将无法被转换为string类型的数组 9 string [] ar = new string [arl.Count]; 10 11 // 使用typeof(string)限定放入数组ar中的数据都是string类型, 12 // 同时再将arl强制转换string类型数组,赋给数组ar 13 ar = ( string [])arl.ToArray( typeof ( string )); 14 foreach ( string a in ar) 15 { 16 Console.Write(a); 17 }
例题:
彩票
1 ArrayList al = new ArrayList(); // 定义一个动态数组 2 Random rd = new Random(); // 定义一个随机数对象 3 do 4 { 5 int i = rd.Next( 1 , 36 ); // 在1-35中产生随机数 6 if ( ! al.Contains (i)) // 如果al不包含随机数i 7 al.Add(i); // 我们就把i添加到动态数组中 8 } while (al.Count < 7 ); // 当动态数组的长度是7的时候,我们停止添加。 9 al.Sort(); // 排序一下动态数组 10 foreach ( object cps in al) 11 Console.WriteLine(cps);// 把这7个数用foreach循环打印出来