集合框架 IList,IList——向量(2)

点击打开链接


五、增加泛型

前面我们通过一个ArrayList类实现了IList接口,通过代码可以知道,ArrayList采用一个object[]类型的数组字段来存储对象引用。这种方式存在两个问题:

  1. Object是引用类型,所以一旦我们向ArrayList集合中存储任何值类型对象,则会发生一次“装箱”操作,.net会将值类型对象转化为引用类型对象;其后每次访问集合中的该对象时,又可能会引起一次“拆箱”操作;
  2. 由于使用Object类型作为对象的引用类型,所以ArrayList无法限制存储对象引用的类型。

看一个例子:

class Program {
    static void Main(string[] args) {
 
        // 声明向量集合对象
        ArrayList lst = new ArrayList();
 
        // 当调用Add方法时, 值类型(int)会自动的"装箱"为引用类型(object)
        lst.Add(100);
 
10         // 当调用索引器时, 返回的是一个object类型的对象引用
11         object o = lst[0];
12  
13         // 要恢复存储前的类型, 这里还需要一次"拆箱"操作
14         int value = (int)o;
15  
16         Console.WriteLine(value);
17  
18         // 这里, 先进行了一次"拆箱"操作, 将lst[0]转化为int类型
19         // 接着进行了一次装箱操作
20         lst[0] = (int)lst[0] + 200;
21         Console.WriteLine(lst[0]);
22  
23         // 对于ArrayList, 可以存放任意类型的对象引用
24         lst.Add("Hello");
25  
26         foreach (object v in lst) {
27             // 这里只能访问object类型的ToString方法
28             // 并无法知道object类型的v变量到底是什么类型
29             Console.WriteLine(v);
30         }
31     }
32 }

通过上述例子,可以清楚的反映出上面提到的两个问题。

.net Framework添加的泛型特性就是为了解决上述问题的。为了支持泛型,我们需要额外的实现一个接口:List<E>接口。

泛型的代码理解起来颇为抽象,下面是代码,童鞋们尽量理解。

using System;
using System.Collections;
using System.Collections.Generic;
 
namespace Edu.Study.Collections.List {
 
    /// <summary>
    /// 定义向量集合类, 实现IList接口
    /// </summary>
10     public class List<E> : IList, IList<E> {
11  
12         /// <summary>
13         /// 泛型迭代子定义, 需要额外实现IEnumerator<E>接口
14         /// </summary>
15         public struct Enumertor<E> : IEnumerator, IEnumerator<E> {
16  
17             /// <summary>
18             /// 表示迭代索引, 即迭代器访问到向量的下标
19             /// </summary>
20             private int index;
21  
22             /// <summary>
23             /// 一个到迭代器所属的向量类对象的引用
24             /// </summary>
25             private List<E> list;
26  
27             /// <summary>
28             /// 参数构造器
29             /// </summary>
30             /// <param name="container">表示迭代器所属的集合类</param>
31             public Enumertor(List<E> container) {
32                 this.list = container;
33                 this.index = -1;
34             }
35  
36             /// <summary>
37             /// 析构器. 后面会讲, 这里空实现
38             /// </summary>
39             public void Dispose() {
40             }
41  
42             /// <summary>
43             /// 实现IEnumerator接口的Current属性
44             ///    由于IEnumerator<T>接口同样具有一个Current属性, 
45             /// 并且同IEnumerator接口的Current属性不同, 所以这里将
46             /// IEnumerator接口的Current属性显示实现
47             /// </summary>
48             object IEnumerator.Current {
49                 get {
50                     return list[index];
51                 }
52             }
53  
54             /// <summary>
55             /// 实现IEnumerator接口的Current属性
56             /// </summary>
57             public E Current {
58                 get {
59                     return list[index];
60                 }
61             }
62  
63             /// <summary>
64             /// 将迭代器指示到“下一个数据位置”
65             /// </summary>
66             public bool MoveNext() {
67                 if (this.index < list.Count) {
68                     ++this.index;
69                 }
70                 return this.index < list.Count;
71             }
72  
73             /// <summary>
74             /// 将迭代器指示恢复到集合起始位置。
75             /// </summary>
76             public void Reset() {
77                 this.index = -1;
78             }
79         }
80  
81         /// <summary>
82         /// 保存数据的数组为泛型类型E类型
83         /// </summary>
84         private E[] array;
85  
86         /// <summary>
87         /// 当前集合的长度
88         /// </summary>
89         private int count;
90  
91         /// <summary>
92         /// 默认构造器
93         /// </summary>
94         public List()
95             : this(1) {
96         }
97  
98         /// <summary>
99         /// 参数构造器
100         /// </summary>
101         public List(int capacity) {
102             if (capacity < 0) {
103                 throw new ArgumentOutOfRangeException("caption");
104             }
105             if (capacity == 0) {
106                 capacity = 1;
107             }
108             this.array = new E[capacity];
109             this.count = 0;
110         }
111  
112         /// <summary>
113         /// 获取向量的长度
114         /// </summary>
115         public int Count {
116             get {
117                 return this.count;
118             }
119         }
120  
121         /// <summary>
122         /// 返回向量实际使用的长度
123         /// </summary>
124         public int Capacity {
125             get {
126                 return this.array.Length;
127             }
128         }
129  
130         /// <summary>
131         /// 是否固定大小的属性(返回常量false)
132         /// </summary>
133         public bool IsFixedSize {
134             get {
135                 return false;
136             }
137         }
138  
139         /// <summary>
140         /// 是否只读集合的属性(返回常量false)
141         /// </summary>
142         public bool IsReadOnly {
143             get {
144                 return false;
145             }
146         }
147  
148         /// <summary>
149         /// 集合是否可同步属性, 返回false, 表示集合无法同步
150         /// </summary>
151         public bool IsSynchronized {
152             get {
153                 return false;
154             }
155         }
156  
157         /// <summary>
158         /// 同步对象属性, 返回null, 表示集合无需同步
159         /// </summary>
160         public object SyncRoot {
161             get {
162                 return null;
163             }
164         }
165  
166         /// <summary>
167         /// 当this.array长度不够时, 重新分配长度足够的新数组。
168         /// </summary>
169         private E[] GetNewArray() {
170  
171             // 这里分配的是泛型E类型的数组
172             return new E[(this.array.Length + 1) * 2];
173         }
174  
175         /// <summary>
176         /// 实现IList<E>接口的Add方法, 该方法采用泛型参数value
177         /// </summary>
178         public void Add(E value) {
179             int newCount = this.count + 1;
180             if (this.array.Length < newCount) {
181  
182                 // 获取新的E类型数组
183                 E[] newArray = GetNewArray();
184                 Array.Copy(this.array, newArray, this.count);
185                 this.array = newArray;
186             }
187  
188             this.array[this.count] = value;
189             this.count = newCount;
190         }
191  
192  
193         /// <summary>
194         /// 向向量的末尾添加对象
195         /// 调用前一个Add方法即可
196         /// </summary>
197         int IList.Add(object value) {
198  
199             // 通过下面这句代码, 看看怎么调用当前类实现另一个接口的同名方法
200             ((IList<E>)this).Add((E)value);
201             return this.count - 1;
202         }
203  
204         /// <summary>
205         /// 实现IList<E>接口的索引器属性, 属性的类型为泛型类型E
206         /// </summary>
207         public E this[int index] {
208             get {
209                 if (index < 0 || index >= this.count) {
210                     throw new ArgumentOutOfRangeException("index");
211                 }
212                 return this.array[index];
213             }
214             set {
215                 if (index < 0 || index >= this.count) {
216                     throw new ArgumentOutOfRangeException("index");
217                 }
218                 this.array[index] = value;
219             }
220         }
221  
222         /// <summary>
223         /// 显示实现IList接口的索引器属性
224         /// 调用前一个索引器属性即可
225         /// </summary>
226         object  IList.this[int index] {
227             get {
228                 return ((IList<E>)this)[index];
229             }
230             set {
231                 ((IList<E>)this)[index] = (E)value;
232             }
233         }
234  
235         /// <summary>
236         /// 删除向量中的元素
237         /// </summary>
238         public void RemoveRange(int index, int count) {
239             if (index < 0) {
240                 throw new ArgumentOutOfRangeException("index");
241             }
242             int removeIndex = index + count;
243             if (count < 0 || removeIndex > this.count) {
244                 throw new ArgumentOutOfRangeException("count");
245             }
246            Array.Copy(this.array, index + 1this.array, index + count - 1this.count - removeIndex);
247             this.count -= count;
248         }
249  
250         /// <summary>
251         /// 实现IList<E>接口的Remove方法
252         /// 返回是否删除元素
253         /// </summary>
254         public bool Remove(E value) {
255             int index = this.IndexOf(value);
256             if (index >= 0) {
257                 this.RemoveRange(index, 1);
258                 return true;
259             }
260             return false;
261         }
262  
263         /// <summary>
264         /// 实现IList接口的Remove方法, 此处为显示实现
265         /// 调用前一个Remove方法即可
266         /// </summary>
267         void IList.Remove(object value) {
268             ((IList<E>)this).Remove((E)value);
269         }
270  
271         /// <summary>
272         /// 从集合的特定位置删除对象的引用
273         /// </summary>
274         public void RemoveAt(int index) {
275             RemoveRange(index, 1);
276         }
277  
278         /// <summary>
279         /// 实现IList<E>接口的IndexOf方法
280         /// </summary>
281         public int IndexOf(E value) {
282             int index = 0;
283             if (value == null) {
284                 while (index < this.count) {
285                     if (this.array[index] == null) {
286                         return index;
287                     }
288                     ++index;
289                 }
290             } else {
291                 while (index < this.count) {
292                     if (value.Equals(this.array[index])) {
293                         return index;
294                     }
295                     ++index;
296                 }
297             }
298             return -1;
299         }
300  
301         /// <summary>
302         /// 实现IList接口的IndexOf方法, 此时为显示实现
303         /// 调用前面的IndexOf方法即可
304         /// </summary>
305         int IList.IndexOf(object value) {
306             return ((IList<E>)this).IndexOf((E)value);           
307         }
308  
309         /// <summary>
310         /// 查找对应的数组项.
311         /// </summary>
312         public int IndexOf(object o, System.Collections.IComparer comparer) {
313             int index = 0;
314             while (index < this.count) {
315                 if (comparer.Compare(this.array[index], o) == 0) {
316                     return index;
317                 }
318                 ++index;
319             }
320             return -1;
321         }
322  
323         /// <summary>
324         /// 弹出向量的最后一个元素(即获取最后一个元素的引用后删除最后一个元素)
325         /// </summary>
326         public object PopBack() {
327             object o = this.array[this.count - 1];
328             RemoveAt(this.count - 1);
329             return o;
330         }
331  
332         /// <summary>
333         /// 弹出向量的第一个对象
334         /// </summary>
335         public object PopFront() {
336             object o = this.array[0];
337             RemoveAt(0);
338             return o;
339         }
340  
341         /// <summary>
342         /// 实现IList<E>接口的Insert方法
343         /// </summary>
344         public void Insert(int index, E value) {
345             if (index >= this.count) {
346                 throw new ArgumentOutOfRangeException("index");
347             }
348             int newCount = this.count + 1;
349             if (this.array.Length < newCount) {
350                 E[] newArray = GetNewArray();
351                 Array.Copy(this.array, newArray, index);
352                 newArray[index] = value;
353                 Array.Copy(this.array, index, newArray, index + 1this.count - index);
354                 this.array = newArray;
355             } else {
356                 Array.Copy(this.array, index, this.array, index + 1this.count - index);
357                 this.array[index] = value;
358             }
359             this.count = newCount;
360         }
361  
362         /// <summary>
363         /// 实现IList接口的Insert方法
364         /// </summary>
365         void IList.Insert(int index, object value) {
366             ((IList<E>)this).Insert(index, (E)value);
367         }
368  
369         /// <summary>
370         /// 实现IList<E>接口的Contains方法
371         /// </summary>
372         public bool Contains(E value) {
373             return this.IndexOf(value) >= 0;
374         }
375  
376  
377         /// <summary>
378         /// 实现IList接口的Contains方法
379         /// </summary>
380         bool IList.Contains(object value) {
381             return ((IList<E>)this).IndexOf((E)value) >= 0;
382         }
383  
384         /// <summary>
385         /// 将内部数组的长度压缩为向量的实际长度
386         /// </summary>
387         public void TrimToSize() {
388             if (this.array.Length > this.count) {
389  
390                 // 这里需要实例化一个泛型数组
391                 E[] newArray = null;
392                 if (this.count > 0) {
393                     newArray = new E[this.count];
394                     Array.Copy(this.array, newArray, this.count);
395                 } else {
396                     newArray = new E[1];
397                 }
398                 this.array = newArray;
399             }
400         }
401  
402         /// <summary>
403         /// 清空向量
404         /// </summary>
405         public void Clear() {
406             this.count = 0;
407         }
408  
409         /// <summary>
410         /// 实现IEnumerable<E>接口的GetEnumerator方法
411         /// </summary>
412         public IEnumerator<E> GetEnumerator() {
413             Enumertor<E> ator = new Enumertor<E>(this);
414             return ator;
415         }
416  
417         /// <summary>
418         /// 实现IEnumerable接口的GetEnumerator方法, 显示实现
419         /// 调用前面的GetEnumerator方法即可
420         /// </summary>
421         IEnumerator IEnumerable.GetEnumerator() {
422             return ((IEnumerable<E>)this).GetEnumerator();
423         }
424  
425         /// <summary>
426         /// 实现ICollection<E>接口的CopyTo方法
427         /// </summary>
428         public void CopyTo(E[] array, int index) {
429             Array.Copy(this.array, 0, array, index, this.count);
430         }
431  
432  
433         /// <summary>
434         /// 实现ICollection接口的CopyTo方法, 显示实现
435         /// </summary>
436         void ICollection.CopyTo(Array array, int index) {
437             Array.Copy(this.array, 0, array, index, this.count);
438         }
439     }
440 }

上述代码展示了一个支持泛型的向量集合,从中我们可以学习到泛型的一些知识。注意以下几点:

  • 通过代码可以看到,将一个不支持泛型的集合类改为支持泛型的集合类非常容易。算法,类结构都无需变化,只需要将原本为object类型的字段改为泛型类型E类型的字段即可。当然访问该字段的所有方法和属性都要进行相应改动。
  • 这个类总共实现了如下接口:IEnumerable, IEnumerable<E>, ICollection, ICollection<E>, IList, IList<E>,迭代子类则实现了IEnumerator和IEnumerator<E>接口。由于这些接口的主要区别在于泛型和非泛型,方法和属性名称都相同,所以采用显式实现的方式来实现非泛型的一系列接口。一方面将这些接口方法隐藏起来,另一方面防止因方法重名造成重载。

现在我们比较一下List<E>和ArrayList使用起来有什么不同。

class Program {
    static void Main(string[] args) {
 
        // 声明向量集合对象
        ArrayList lst = new ArrayList();
 
        // 当调用Add方法时, 值类型(int)会自动的"装箱"为引用类型(object)
        lst.Add(100);
 
10         // 当调用索引器时, 返回的是一个object类型的对象引用
11         object o = lst[0];
12  
13         // 要恢复存储前的类型, 这里还需要一次"拆箱"操作
14         int value = (int)o;
15  
16         Console.WriteLine(value);
17  
18         // 这里, 先进行了一次"拆箱"操作, 将lst[0]转化为int类型
19         // 接着进行了一次装箱操作
20         lst[0] = (int)lst[0] + 200;
21         Console.WriteLine(lst[0]);
22  
23         // 对于ArrayList, 可以存放任意类型的对象引用
24         lst.Add("Hello");
25  
26         foreach (object v in lst) {
27             // 这里只能访问object类型的ToString方法
28             // 并无法知道object类型的v变量到底是什么类型
29             Console.WriteLine(v);
30         }
31  
32         // 这里使用了List<E>类, 和ArrayList类比较下, 看区别在哪里
33         List<int> slst = new List<int>();
34         slst.Add(100);
35         slst.Add(200);
36         foreach (int n in slst) {
37             Console.WriteLine(n);
38         }
39     }
40 }

本章代码下载

看,泛型确定并强制了集合存储的对象类型,并且避免了装箱拆箱操作。既高效又安全。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值