关于C#中集合和数组(二)

时间:2022-04-05 05:00:57

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());

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. 常用的属性、方法

 

常用属性

属性说明

 

Comparer

获取用于确定字典中的键是否相等的 IEqualityComparer

 

Count

获取包含在 Dictionary 中的键/值对的数目。

 

Item

获取或设置与指定的键相关联的值。

 

Keys

获取包含 Dictionary 中的键的集合。

 

Values

获取包含 Dictionary 中的值的集合。

 

常用的方法

方法说明

 

Add

将指定的键和值添加到字典中。

 

Clear

Dictionary 中移除所有的键和值。

 

ContainsKey

确定 Dictionary 是否包含指定的键。

 

ContainsValue

确定 Dictionary 是否包含特定值。

 

Equals 

已重载。 确定两个 Object 实例是否相等。 (从 Object 继承。)

 

GetEnumerator

返回循环访问 Dictionary 的枚举数。

 

GetHashCode 

用作特定类型的哈希函数。GetHashCode 适合在哈希算法和数据结构(如哈希表)中使用。 (从Object 继承。)

 

GetObjectData

实现 System.Runtime.Serialization.ISerializable 接口,并返回序列化Dictionary 实例所需的数据。

 

GetType 

获取当前实例的 Type。 (从 Object 继承。)

 

OnDeserialization

实现 System.Runtime.Serialization.ISerializable 接口,并在完成反序列化之后引发反序列化事件。

 

ReferenceEquals 

确定指定的 Object 实例是否是相同的实例。 (从 Object 继承。)

 

Remove

Dictionary 中移除所指定的键的值。

 

ToString 

返回表示当前 ObjectString。 (从 Object 继承。)

 

TryGetValue

获取与指定的键相关联的值。

 

 

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("马蜂");