理解List

 

首先开始的就是对List的重新认知。在这里,让我们先从构造方法来重新认识List<T>的本质,先来看下上文中我所粘出的代码

List<int> list =newList<int>();for(inti = 0; i < 10; i++)
{
    list.Add(i);
}           
Random r =newRandom();for(intj = 0; j < 100; j++)
{inttemp;intx1 = r.Next(10);intx2 = r.Next(10);
    temp = list[x1];
    list[x1] = list[x2];
    list[x2] = temp;
}
在上文中,我对这个List大批特批,现在,我们来重新看下这个List的构造:
publicList()
{this._items =List<T>._emptyArray;
}

先来看无参的构造方法,当无参的时候,.NET Framework其实是用一个_emptyArray来初始化了List中的items集合。那么_emptyArray又是什么呢?我们继续向下看:
private staticT[] _emptyArray;

恩,他是一个静态字段,然后我们看下List<T>的静态构造方法:

staticList()
{List<T>._emptyArray =newT[0];
}

我们看到,_emptyArray其实是一个T类型的数组,个数为0。那么也就是说,当我们执行0参数的构造方法时,系统是把items集合给赋值为了一个T类型的个数为0的数组。

那么items又是什么?我们继续向下看:

public voidAdd(T item)
{if(this._size ==this._items.Length)
    {this.EnsureCapacity(this._size + 1);
    }this._items[this._size++] = item;this._version++;
}

这是List<T>中一个Add(T item)方法,但是我们可以从方法中敲出些端倪来。

在这里,我并不是想解释这个方法的原理,只是想说,在List中,其实维护这一个items,然后很多操作,是基于items的操作。

恩,所以在上文中,List<int> list=new List<int>();和Array a=new int[10]();的操作其实差别并不大。

我们肯定还记得在《Effective C#》中有这样一条规则,就是说:在初始化List之前最好对List初始化大小。

让我们从源码中来找到这一条规则的答案。

private voidEnsureCapacity(intmin)
{if(this._items.Length < min)
    {intnum = (this._items.Length == 0) ? 4 :
%P3mg1v6z:B g!{$|0(this._items.Length * 2);if(num < min)
        {
            num = min;
        }this.Capacity = num;
    }
}

我们来看,在这个方法体中,List会新建一个数组,然后把数组的长度设置为原来的二倍(如果原有的数组长度为0,那就默认将数组的长度设置为4)。

因此,这种,让List的方法自己去调用EnsureCapacity(int min)方法,不仅浪费了构造数组的时间,还浪费了大量的空间(因为将原有的数组空间扩充了二倍)。

因此,请记得:在初始化List之前最好指定List的大小。

为了证明上述的观点,我们再来随便看一些代码:

public intIndexOf(T item)
{returnArray.IndexOf<T>(this._items, item, 0,this._size);
}

 
public intFindIndex(intstartIndex,intcount,Predicate<T> match)
{if(startIndex >this._size)
    {
        ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.startIndex, ExceptionResource.ArgumentOutOfRange_Index);
    }if((count < 0) || (startIndex > (this._size - count)))
    {
        ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.count, ExceptionResource.ArgumentOutOfRange_Count);
    }if(match ==null)
    {
        ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match);}  intnum = startIndex + count;for(inti = startIndex; i < num; i++)
    {if(match(this._items[i]))
        {returni;
        }
    }return-1;
} 

由上面的代码,我想证明的是:其实List<T>不过是对Array的进一步封装,说得再直接点,我愿意理解List<T>为Array的可扩充版本,然后扩展了一些方法;

那关于Array和List的选择,我重新做一个说明:

List是基于Array存在的,因此,在创建一个List对象时,需要耗费比Array相对更多的时间,以及更大的空间,因为List除了初始化内部的items外还需要初始化一些其他的属性。而且在方法调用时,这点我没有证实,只是一个猜测,List需要的是再去调用Array的相关方法,因此也许会存在方法调用的时间消耗问题。

因此,我的建议是:

如果初始化时确定大小,那么就使用Array。

如果初始化时不确定大小,那么就使用List。当然,其实完全可以自己去实现List中的数组扩充功能的,也许会更棒,因为我们没有必要去将Array每次都扩充为原来的二倍。

另外,非主流程序员的补充,Array相对于List还有个优势就是:多维数组比List的嵌套更容易理解,也就是说int[][](或者是int[,])要强于List>,也就说在类型确定且多维的情况下,用Array要优于List。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值