ArrayList
动态数组ArrayList.ArrayList类提供了继承了IList接口。
ILsit接口成员:Add、Insert、RemoveAt、Remove、Contains、Clear、IndexOf方法,它最大的特色在于提供类似数组索引的访问机制。
◆ 数组的容量是固定的,而ArrayList的容量可根据需要自动扩充,定义是如果未设置容量,向ArrayList添加结束后,容量为初始化的2倍,定义时设置容量,超出时,该容量自动翻倍。如果更改了Capacity属性的值,则可以自动进行内存重新分配和元素复制。
◆ ArralyList提供添加、插入或移除某一范围元素的方法。在数组中,只能一次获取或设置一个元素的值。
◆ 使用Synchronized方法很容易创建ArrayList的同步版本。数组将实现同步的任务留给了用户。
◆ ArrayList提供将只读和固定大小包装返回到集合的方法;而数组则不提供该方法。
◆ ArrayList只提供一维的形式,而数组可以是多维的。
1. ArrayLlst的常用属性:
属性 说明
Capacity 获取或设置ArrayList可包含的元素数
Count 获取ArrayList中实际包含的元素数
IsFixedSize 获取一个值,该值指示ArrayList是否具有固定大小
IsReadOnly 获取一个值,该值指示ArrayList是否为只读
IsS_ynchrOnlzed 获取一个值,该值指示是否同步对ArrayList的访问
Item 获取或设置指定索引处的元素
SyncRoot 获取可用于同步ArrayList访问的对象
2. ArrayList元素的添加
C#中为ArrayList提供了两个添加元素的方法,分别为ArrayList.Add();和ArrayList.Insert();
A、ArrayList.Add():该方法将给定的Value对象插入到ArrayList的现有数据的末尾处。例:
ArrayList arrayList = new ArrayList(3);
arrayList.Add("WWW");
arrayList.Add("002");
Console.Write(arrayList.Capacity.ToString());//输出arrayList的容量
//输出arrayList中元素的个数
Console.Write(arrayList.Count.ToString());
//输出arrayList索引为0的值:"WWW"
Console.Write(arrayList[0].ToString());
//输出arrayList索引为1的值:"002"
Console.Write(arrayList[1].ToString());
当ArrayList为固定大小或只读时,如果对其进行元素的插入操作或超出固定大小时,则将会引发NotSupportExCeption异常。解决办法是先检测ArrayList是否为只读或固定大小,之后再进行插入,可以使用ArrayList.IsFixedSize和ArrayList.IsReadOnly进行判断。
B、ArrayList.Insert():该方法向指定的索引位置插入value,如果其后有元素将自动向后错开。由于index参数的存在,因此,Insert方法比Add多了一个可能产生的异常。该异常为ArgumentoutofRangeExceptlon,当index小于零或大于ArrayList中的元素个数时引发。例:
ArrayListarrayList = new ArrayList(3);
//初始添加插入到ArrayList的现有数据的末尾处:索引为0
arrayList.Add("WWW");
//初始添加插入到ArrayList的现有数据的末尾处:索引为1
arrayList.Add("002");
arrayList.Insert(1, "Wu"); //添加位置为索引为1
arrayList.Insert(2, "zy"); //添加位置为索引为2
//输出arrayList的容量:6
Console.Write(arrayList.Capacity.ToString());
//输出arrayList中元素的个数:4
Console.Write(arrayList.Count.ToString());
//输出arrayList索引为0的值:"WWW"
Console.Write(arrayList[0].ToString());
//输出arrayList索引为1的值:"Wu"
Console.Write(arrayList[1].ToString());
//输出arrayList索引为2的值:"zy"
Console.Write(arrayList[2].ToString());
//输出arrayList索引为3的值:"002"
Console.Write(arrayList[3].ToString());
在指定的位置成功地插入了指定的字符串。同时,由于ArrayList的初始容量为3,而所有插入的元素个数为4,当插入第4个元素的时候ArrayList的容量翻倍,因此,运行结果中显示容量大小为6。
C、集体添加
//集体添加方法一
ArrayListarrayList = new ArrayList(3);
foreach (int numberin new int[6] { 9, 3, 7,2, 4, 8 })
arrayList.Add(number);
//集体添加方法二
int[] number2 = new int[2] { 11, 12};
arrayList.AddRange(number2);
//新ArrayList只取旧ArrayList一部份
ArrayListal2 = new ArrayList(arrayList.GetRange(0,2));
3、ArrayList元素的删除
C#中为删除ArrayList元素提供了以下几种方法
◆ ArrayList.Clear():
为一个不带参数的方法,功能为从ArrayList中移除所有元素
ArrayListarrayList = new ArrayList(3);
arrayList.Add("WWW");//初始添加插入到ArrayList的现有数据的末尾处:索引为0
arrayList.Add("002");//初始添加插入到ArrayList的现有数据的末尾处:索引为1
Console.Write(arrayList.Capacity.ToString()); //输出arrayList的容量:3
Console.Write(arrayList.Count.ToString()); //输出arrayList中元素的个数:2
arrayList.Clear(); //清空arrayList
//输出arrayList的容量:3
Console.Write(arrayList.Capacity.ToString());
//输出arrayList中元素的个数:0
Console.Write(arrayList.Count.ToString());
u ArrayList.Remove():
从指定的ArrayList 中移除obj对象,如obj不存在则不引发异常,ArrayList保持不变。
ArrayList arrayList = new ArrayList(3);
arrayList.Add("WWW"); //初始添加插入到ArrayList的现有数据的末尾处:索引为0
arrayList.Add("002"); //初始添加插入到ArrayList的现有数据的末尾处:索引为1
Console.Write(arrayList.Capacity.ToString()); //输出arrayList的容量:3
Console.Write(arrayList.Count.ToString()); //输出arrayList中元素的个数:2
arrayList.Remove("WWW"); //移除object (WWW,有,移除该对象)
arrayList.Remove("003"); //移除object (003,没有,不报异常,ArrayList保持不变)
Console.Write(arrayList.Capacity.ToString()); //输出arrayList的容量:3
Console.Write(arrayList.Count.ToString()); //输出arrayList中元素的个数:1
◆ ArrayList.RemoveAt():
提供了根据索引值移除ArrayList元素的方法。
ArrayList arrayList = new ArrayList(3);
arrayList.Add("WWW"); //初始添加插入到ArrayList的现有数据的末尾处:索引为0
arrayList.Add("002"); //初始添加插入到ArrayList的现有数据的末尾处:索引为1
Console.Write(arrayList.Capacity.ToString()); //输出arrayList的容量:3
Console.Write(arrayList.Count.ToString()); //输出arrayList中元素的个数:2
arrayList.RemoveAt(0);//根据索引移除 (【0】,有,移除该对象,其后有数据往前补)
arrayList.RemoveAt(1);//根据索引移除 (【1】,没有,报异常:索引超出范围。必须为非负值并小于集合大小)
Console.Write(arrayList.Capacity.ToString()); //输出arrayList的容量:3
Console.Write(arrayList.Count.ToString()); //输出arrayList中元素的个数:1
4. ArrayList元素的获取
A、通过arrayList[index] 取出,取出时为object类型。
ArrayList arrayList = new ArrayList();
arrayList.Add("WWW"); //初始添加插入到ArrayList的现有数据的末尾处:索引为0
arrayList.Add("002"); //初始添加插入到ArrayList的现有数据的末尾处:索引为1
Console.Write(arrayList[0].ToString());//输出arrayList索引的【0】的值(存在,输出)
Console.Write(arrayList[2].ToString());//输出arrayList索引的【2】的值(不存在,报异常:索引超出范围)
B、使用与数组相同的方法ArrayList进行遍历,即foreach语句
ArrayList arrayList = new ArrayList();
arrayList.Add("WWW");
arrayList.Add("002");
//遍历方法一:遍历输出(不需要强转)
foreach (string ainarrayList)
Console.Write(a);
//遍历方法二
for (int i= 0; i < arrayList.Count; i++)
{
//一定要强制转换 (根据添加的类型)
int number = (int)arrayList[i];Console.WriteLine(number);
}
5. 常用方法
1)Reverse() 反转数组的元素
2)Sort() 以从小到大的顺序排列数组的元素
3) Clone() 复制一个数组
HashTable
在.NETFramework中,Hashtable是System.Collections命名空间提供的一个容器,用于处理和表现类似key/value的键值对,其中key通常可用来快速查找,同时key是区分大小写;value用于存储对应于key的值。它表示键(key)/值(value)对的集合,这些键/值对根据键的哈希代码进行组织。它的每个元素都是一个存储在字典实体对象中的键/值对。键不能为空引用,但值可以。Hashtable中key/value键值对均为object类型,所以Hashtable可以支持任何类型的key/value键值对。也就是说HashTable像一个字典,根据键可以查找到相应的值。HashTable中的key/value均为object类型,由包含集合元素的存储桶组成。存储桶是 HashTable中各元素的虚拟子组,与大多数集合中进行的搜索和检索相比,存储桶可令搜索和检索更为便捷。每一存储桶都与一个哈希代码关联,该哈希代码是使用哈希函数生成的并基于该元素的键。HashTable是C#中一个较为复杂的类型,其构造函数就有16种之多,只介绍两种简单、实用的供参考:
◆HashTable()
构造函数定义使用默认的初始容量等默认值初始化HashTable类的新的空实例。同ArrayList一样,其容量可以根据实际的需要自动增加。
HashTablemyHashTablel =new HashTable();
◆HashTab]e(Int32)
构造函数使用指定的容量初始化HashTable类的新实例。此处指定的容量是指对象最初可包含的元素的近似数目,其容量以后可以自动增加。
HashTablemyHashTable2 =new HashTable(5);
1. HashTable元素的添加
C#为HashTable元素的添加提供了HashTable.Add(key,value);方法。该方法的定义如下其中, key为要添加的元素的键,value为要添加元素的值,可以为空。HashTable对于插入的键/值对没有具体的要求,可以是任意对象。
Hashtable hashTable = new Hashtable();
hashTable.Add(1,"WWW"); //插入到Hashtable:key为1,int型
hashTable.Add(5,"PPP"); //插入到Hashtable:key为5,int型
hashTable.Add("my", "Wu");//插入到Hashtable:key为“my”,string型
Console.Write(hashTable.Count.ToString());//获取Hashtable的键值对个数
2. HashTable元素的获取
与数组和ArrayList不同,HashTable不可以通过HashTable[index]的方式获取元素。HashTable是以一种键/值对的形式存在的,因此要通过键来访问HashTable中的值,即[key].
例:string s=(string)hashTable ["my"];
[key]存在则输出值。不存在,报异常:未将对象引入实例
1) C#中为HashTable的查找判断提供了以下方法:HashTable.Contains和HashTable.ContainsKey 方法实现的功能相同,皆为判断HashTable中是否包含指定的键。HashTable.ContainsValue为判断HashTable中是否包含指定的值。
◆HashTable.Contains
◆HashTable.ContainsKey
◆HashTable.ContalnsValue
HashtablehashTable =new Hashtable();
hashTable.Add(1, "WWW");
hashTable.Add(5, "PPP");
hashTable.Add("my","Wu");
Console.Write(hashTable.Contains(1));//判断hashTable中是否存在key值为【1】(存在,返回true)
Console.Write(hashTable.ContainsKey("my")); //判断hashTable中是否存在key值为【"my"】 (存在,返回true)
Console.Write(hashTable.ContainsValue("ppp"));//判断hashTable中是否存在Value值为【"ppp"】 (不存在,返回false)
1) HashTable 的遍历
C#中提供了foreach语句以对HashTable进行遍历。由于HashTable的元素是一个键/值对,因此需要使用DictionaryEntry类型来进行遍历。
HashtablehashTable =new Hashtable();
hashTable.Add(1, "WWW");//插入到Hashtable:key为1,int型
hashTable.Add(5, "PPP"); //插入到Hashtable:key为5,int型
hashTable.Add("my","Wu");//插入到Hashtable:key为“my”,string型
foreach(DictionaryEntry hsinhashTable)
{
Console.Write("key:" +hs.Key.ToString() +",value:" +hs.Value.ToString() + ";");
}
3. HashTable元素的删除
C#为HashTable中元素的删除提供了两种方法HashTable.Clear()和HashTable.Remove(),下面分别介绍这两种方法。
1) HashTable.Clear()可以从Hashtable中移除所有元素,此方法不带任何参数。例:
HashtablehashTable =new Hashtable();
hashTable.Add(1, "WWW");
hashTable.Add(5, "PPP");
hashTable.Add("my","Wu");
Console.Write(hashTable.Count.ToString()); //获取Hashtable的键值对个数:3
hashTable.Clear(); //清空hashTable
Console.Write(hashTable.Count.ToString()); //获取Hashtable的键值对个数:0
2) HashTable.Remove(object)方法,用于从Hashtable中移除带有指定键的元素
HashtablehashTable = new Hashtable();
hashTable.Add(1, "WWW");
hashTable.Add(5, "PPP");
hashTable.Add("my","Wu");
Console.Write(hashTable.Count.ToString()); //获取Hashtable的键值对个数:3
hashTable.Remove(1); //清除hashTable中指定键为【key:1】的元素(存在,清除)
hashTable.Remove("1");//清除hashTable中指定键【key:"1"】的元素(不存在,不报异常)
Console.Write(hashTable.Count.ToString()); //获取Hashtable的键值对个数:2
4. HashTable优点
HashTable的原理:通过节点的关键码确定节点的存储位置,即给定节点的关键码k,通过一定的函数关系H(散列函数),得到函数值H(k),将此值解释为该节点的存储地址。因此,HashTable的优点就在于其索引的方式,不是通过简单的索引号,而是采用一个键(key)。这样可以方便地查找HashTable中的元素。另外,HashTable带来的好处就是其查找速度非常快,在对查找速度要求比较高的场合可以考虑使用HashTable。如果以任意类型键值访问其中元素会快于其他集合。
HashTable是经过优化的,访问下标的对象先散列过,所以内部是无序散列的,保证了高效率,也就是说,其输出不是按照开始加入的顺序,而Dictionary遍历输出的顺序,就是加入的顺序,这点与Hashtable不同。如果一定要排序HashTable输出,只能自己实现:HashTable排序。
5. HashTable排序
对哈希表进行排序在这里的定义是对key/value键值对中的key按一定规则重新排列,但是实际上这个定义是不能实现的,因为我们无法直接在Hashtable进行对key进行重新排列,如果需要Hashtable提供某种规则的输出,可以采用一种变通的做法:
Hashtable hashTable = newHashtable();
hashTable.Add("23","WWW");
hashTable.Add("15","PPP");
hashTable.Add("my","Wu");
ArrayListarrayList = new ArrayList(hashTable.Keys); //hashTable进行排序
arrayList.Sort();
foreach(string alinarrayList)
{
Console.Write("Key:" + al +"——>"+ hashTable[al] + " ;");
}
6.应用场合
HashTable的优点就在于其索引的方式,速度非常快。如果以任意类型键值访问其中元素会快于其他集合,特别是当数据量特别大的时候,效率差别尤其大。HashTable的应用场合有:做对象缓存,树递归算法的替代,和各种需提升效率的场合。
7. Hashtable和 Dictionary <K, V> 类型
1).单线程程序中推荐使用 Dictionary, 有泛型优势, 且读取速度较快, 容量利用更充分.
2).多线程程序中推荐使用 Hashtable,默认的 Hashtable 允许单线程写入, 多线程读取, 对 Hashtable进一步调用 Synchronized() 方法可以获得完全线程安全的类型. 而 Dictionary 非线程安全, 必须人为使用 lock 语句进行保护,效率大减.
3) Dictionary 有按插入顺序排列数据的特性 (注:但当调用 Remove() 删除过节点后顺序被打乱), 因此在需要体现顺序的情境中使用 Dictionary 能获得一定方便.
Dictionary和HashTable内部实现差不多,但前者无需装箱拆箱操作,效率略高一点。
Dictionary<int,string>fruits = newDictionary<int,string>();
fruits.Add(1,"apple");
fruits.Add(2,"banana");
fruits.Add(3,"orange");
foreach (int iin fruits.Keys)
{
Console.Write("key:{0} value:{1}",i, fruits);
}
if (fruits.ContainsKey(1))
{
Console.Write("contain this key.");
}
8. HashTable与线程安全
为了保证在多线程的情况下的线程同步访问安全,微软提供了自动线程同步的HashTable: 如果 HashTable要允许并发读但只能一个线程写, 就要这么创建 HashTable实例:
//Thread safe HashTable
HashtablehtSyn = Hashtable.Synchronized(newHashtable());
这样, 如果有多个线程并发的企图写HashTable里面的 item, 则同一时刻只能有一个线程写, 其余阻塞; 对读的线程则不受影响。
另外一种方法就是使用lock语句,但要lock的不是HashTable,而是其SyncRoot;虽然不推荐这种方法,但效果一样的,因为源代码就是这样实现的:
//Thread safe
private HashtablehtCache = new Hashtable();
public staticvoidAccessCache()
{
lock (htCache.SyncRoot)
{
htCache.Add("key","value");
}
}
//Is equivalent to 等同于 (lock is equivalent to Monitor.Enter and Exit()
public staticvoidAccessCache()
{
System.Threading.Monitor.Enter(htCache.SyncRoot);
try
{
htCache.Add("key","value");
}
finally
{
System.Threading.Monitor.Exit(htCache.SyncRoot);
}
}
Dictionary
泛型最常见的用途是泛型集合,命名空间System.Collections.Generic 中包含了一些基于泛型的集合类,使用泛型集合类可以提供更高的类型安全性,还有更高的性能,避免了非泛型集合的重复的装箱和拆箱。
很多非泛型集合类都有对应的泛型集合类,常用的非泛型集合类以及对应的泛型集合类:
非泛型集合类 |
泛型集合类 |
ArrayList |
List<T> |
HashTable |
DIctionary<T> |
Queue |
Queue<T> |
Stack |
Stack<T> |
SortedList |
SortedList<T> |
我们用的比较多的非泛型集合类主要有 ArrayList类 和HashTable类。我们经常用HashTable 来存储将要写入到数据库或者返回的信息,在这之间要不断的进行类型的转化,增加了系统装箱和拆箱的负担,如果我们操纵的数据类型相对确定的化用 Dictionary<TKey,TValue> 集合类来存储数据就方便多了,例如我们需要在电子商务网站中存储用户的购物车信息( 商品名,对应的商品个数)时,完全可以用 Dictionary<string, int> 来存储购物车信息,而不需要任何的类型转化。
下面是简单的例子,包括声明,填充键值对,移除键值对,遍历键值对
Dictionary<string,string>myDic = newDictionary<string,string>();
myDic.Add("aaa", "111");
myDic.Add("bbb", "222");
myDic.Add("ccc", "333");
myDic.Add("ddd", "444");
1. 元素添加
//如果添加已经存在的键,add方法会抛出异常
try
{
myDic.Add("ddd", "ddd");
}
catch (ArgumentException ex)
{
Console.WriteLine("此键已经存在:" + ex.Message);
}
//解决add()异常的方法是用ContainsKey()方法来判断键是否存在
if (!myDic.ContainsKey("ddd"))
{
myDic.Add("ddd", "ddd");
}
else
{
Console.WriteLine("此键已经存在:");
}
2. 元素赋值修改
//而使用索引器来负值时,如果建已经存在,就会修改已有的键的键值,而不会抛出异常
myDic["ddd"] = "ddd";
myDic["eee"] = "555";
3. 元素取值
//使用索引器来取值时,如果键不存在就会引发异常
try
{
string fff = myDic["fff"];
Console.WriteLine("不存在的键\"fff\"的键值为:"+ fff);
}
catch (KeyNotFoundException ex)
{
Console.WriteLine("没有找到键引发异常:" + ex.Message);
}
//解决上面的异常的方法是使用ContarnsKey()来判断时候存在键,如果经常要取健值得化最好用TryGetValue方法来获取集合中的对应键值
string value = "";
if (myDic.TryGetValue("fff",outvalue))
{
Console.WriteLine("不存在的键\"fff\"的键值为:"+ value);
}
else
{
Console.WriteLine("没有找到对应键的键值");
}
4. 遍历
//下面用foreach来遍历键值对
//【泛型结构体】 用来存储健值对
foreach (KeyValuePair<string,string> kvpin myDic)
{
Console.WriteLine("key={0},value={1}", kvp.Key,kvp.Value);
}
//获取值得集合
foreach (string sinmyDic.Values)
{
Console.WriteLine("value={0}", s);
}
//获取值得另一种方式
Dictionary<string,string>.ValueCollection values = myDic.Values;
foreach (string sin values)
{
Console.WriteLine("value={0}", s);
}
5. 常用的属性、方法
|
常用属性 |
属性说明 |
|
获取用于确定字典中的键是否相等的 IEqualityComparer。 |
|
|
获取包含在 Dictionary 中的键/值对的数目。 |
|
|
获取或设置与指定的键相关联的值。 |
|
|
获取包含 Dictionary 中的键的集合。 |
|
|
获取包含 Dictionary 中的值的集合。 |
|
|
常用的方法 |
方法说明 |
|
将指定的键和值添加到字典中。 |
|
|
从 Dictionary 中移除所有的键和值。 |
|
|
确定 Dictionary 是否包含指定的键。 |
|
|
确定 Dictionary 是否包含特定值。 |
|
|
||
|
返回循环访问 Dictionary 的枚举数。 |
|
|
用作特定类型的哈希函数。GetHashCode 适合在哈希算法和数据结构(如哈希表)中使用。 (从Object 继承。) |
|
|
实现 System.Runtime.Serialization.ISerializable 接口,并返回序列化Dictionary 实例所需的数据。 |
|
|
||
|
实现 System.Runtime.Serialization.ISerializable 接口,并在完成反序列化之后引发反序列化事件。 |
|
|
确定指定的 Object 实例是否是相同的实例。 (从 Object 继承。) |
|
|
从 Dictionary 中移除所指定的键的值。 |
|
|
||
|
获取与指定的键相关联的值。 |
List
可通过索引访问的对象的强类型列表。提供用于对列表进行搜索、排序和操作的方法,在决定使用 List 还是使用 ArrayList 类(两者具有类似的功能)时,记住 List 类在大多数情况下执行得更好并且是类型安全的。如果对 List 类的类型 T 使用引用类型,则两个类的行为是完全相同的。但是,如果对类型 T 使用值类型,则需要考虑实现和装箱问题。
如果对类型 T 使用值类型,则编译器将特别针对该值类型生成List 类的实现。这意味着不必对 List 对象的列表元素进行装箱就可以使用该元素,并且在创建大约 500 个列表元素之后,不对列表元素装箱所节省的内存将大于生成该类实现所使用的内存。
//声明一个List对象,只加入string参数
List<string> names =newList<string>();
names.Add("乔峰");
names.Add("欧阳峰");
names.Add("马蜂");
//遍历List
foreach (string namein names)
{
Console.WriteLine(name);
}
//向List中插入元素
names.Insert(2,"张三峰");
//移除指定元素
names.Remove("马蜂");