建议16:元素数量可变的情况下不应使用数组

在C#中,数组一旦被创建,长度就不能改变。如果我们需要一个动态且可变长度的集合,就应该使用ArrayList或List<T>来创建。 而数组本身,尤其是一维数组,在遇到要求高效率的算法时,则会专门被优化以提升其效率。一维数组也成为向量,其性能是最佳的,在IL中使用了专门的指令来 处理它们(如newarr、ldelem、ldelema、ldelen和stelem)。

从内存的使用角度来讲,数组在创建时被分配了一段固定长度的内存。如果数组的元素是值类型,则每个元素的长度等于相应的值类型的长度;如果数组的元素是引用类型,则每个元素的长度为该引用类型的IntPtr.Size(IntPtr:It's a class that wraps a pointer that is used when calling Windows API functions. The underlying pointer may be 32 bit or 64 bit, depending on the platform.)。数组的存储结构一旦被分配,就不能再变化。而ArrayList是链表结构,可以动态的增减内存空间,如果ArrayList存储的是值类型,则会为每个元素增加12字节的空间,其中4个字节拥有对象引用,8字节是元素装箱时引入的对象头。List<T>是ArrayList的泛型实现,它省去了装箱和拆箱带来的开销。

注意:

由于数组本身在内存上的特点,因此在使用数组的过程中还应该注意大对象的问题。所谓“大对象”,是指那些占内存超过85000字节的对象,它们被分配在大对象堆里。大对象的分配和回收和小对象都不太一样,尤其是回收,大对象在回收过程中会带来效率很低的问题。所以,不能对数组指定过大的长度,这会让数组成为一个大对象。

如果一定要动态改变数组的长度,一种方法是将数组转换为ArrayList或List<T>,如下面代码说是:

            int[] iArr = { , , , , , ,  };
ArrayList arrayListInt = new ArrayList(iArr); //将数组转变为ArrayList
arrayListInt.Add();
List<int> listInt = iArr.ToList<int>(); //将数组转变为List<T>
listInt.Add();

还有一种方法是数组的复制功能。数组继承自System.Array,抽象类System.Array提供了一些有用的实现方法。其中就包括Copy方法,它负责将一个数组的内容复制到另外一个数组中。无论哪种方法,改变数组长度就相当于重新创建了一个数组对象。

为了让数组看上去本身就具有动态改变长度的功能,可以创建一个名为Resize的扩展方法,代码如下所示:

    public static class ClassForExtensions
{
public static Array ReSize(this Array array, int newSize)
{
Type t = array.GetType().GetElementType();
Array newArray = Array.CreateInstance(t, newSize);
Array.Copy(array, , newArray, , Math.Min(array.Length, newSize));
return newArray;
}
}

调用方法看起来如下:

int[] iArr = { , , , , , ,  };
iArr = (int[])iArr.ReSize();

下面对改变数组长度和改变Lisit<T>长度的耗时做一个比较,以便强调本建议的主题:在元素数量可变的情况下不应该使用数组。

        private static void ResizeArray()
{
int[] iArr = { , , , , , , };
Stopwatch watch = new Stopwatch();
watch.Start();
iArr = (int[])iArr.ReSize();
watch.Stop();
Console.WriteLine("ResizeArray: " + watch.Elapsed);
} private static void ResizeList()
{
List<int> iArr = new List<int>(new int[] { , , , , , , });
Stopwatch watch = new Stopwatch();
watch.Start();
iArr.Add();
iArr.Add();
iArr.Add();
watch.Stop();
Console.WriteLine("ResizeList: " + watch.Elapsed);
}

输出为:

ResizeArray:00:00:00.0004441

ResizeList:00:00:0:0000036

当然,严格意义上讲,List<T>不存在改变长度的说法,本建议只是为了比较,将iArr的长度变为10,同时还进行了赋值。即便这样,我们可以看到,在时间效率上ResizeList比ResizeArray要高100倍以上。

转自:《编写高质量代码改善C#程序的157个建议》陆敏技

编写高质量代码改善C#程序的157个建议——建议16:元素数量可变的情况下不应使用数组的更多相关文章

  1. 编写高质量代码改善C#程序的157个建议[1-3]

    原文:编写高质量代码改善C#程序的157个建议[1-3] 前言 本文主要来学习记录前三个建议. 建议1.正确操作字符串 建议2.使用默认转型方法 建议3.区别对待强制转换与as和is 其中有很多需要理 ...

  2. 读书--编写高质量代码 改善C#程序的157个建议

    最近读了陆敏技写的一本书<<编写高质量代码  改善C#程序的157个建议>>书写的很好.我还看了他的博客http://www.cnblogs.com/luminji . 前面部 ...

  3. 编写高质量代码改善C#程序的157个建议——建议157:从写第一个界面开始,就进行自动化测试

    建议157:从写第一个界面开始,就进行自动化测试 如果说单元测试是白盒测试,那么自动化测试就是黑盒测试.黑盒测试要求捕捉界面上的控件句柄,并对其进行编码,以达到模拟人工操作的目的.具体的自动化测试请学 ...

  4. 编写高质量代码改善C#程序的157个建议——建议156:利用特性为应用程序提供多个版本

    建议156:利用特性为应用程序提供多个版本 基于如下理由,需要为应用程序提供多个版本: 应用程序有体验版和完整功能版. 应用程序在迭代过程中需要屏蔽一些不成熟的功能. 假设我们的应用程序共有两类功能: ...

  5. 编写高质量代码改善C#程序的157个建议——建议155:随生产代码一起提交单元测试代码

    建议155:随生产代码一起提交单元测试代码 首先提出一个问题:我们害怕修改代码吗?是否曾经无数次面对乱糟糟的代码,下决心进行重构,然后在一个月后的某个周一,却收到来自测试版的报告:新的版本,没有之前的 ...

  6. 编写高质量代码改善C#程序的157个建议——建议154:不要过度设计,在敏捷中体会重构的乐趣

    建议154:不要过度设计,在敏捷中体会重构的乐趣 有时候,我们不得不随时更改软件的设计: 如果项目是针对某个大型机构的,不同级别的软件使用者,会提出不同的需求,或者随着关键岗位人员的更替,需求也会随个 ...

  7. 编写高质量代码改善C#程序的157个建议——建议153:若抛出异常,则必须要注释

    建议153:若抛出异常,则必须要注释 有一种必须加注释的场景,即使异常.如果API抛出异常,则必须给出注释.调用者必须通过注释才能知道如何处理那些专有的异常.通常,即便良好的命名也不可能告诉我们方法会 ...

  8. 编写高质量代码改善C#程序的157个建议——建议152:最少,甚至是不要注释

    建议152:最少,甚至是不要注释 以往,我们在代码中不写上几行注释,就会被认为是钟不负责任的态度.现在,这种观点正在改变.试想,如果我们所有的命名全部采用有意义的单词或词组,注释还有多少存在的价值. ...

  9. 编写高质量代码改善C#程序的157个建议——建议151:使用事件访问器替换公开的事件成员变量

    建议151:使用事件访问器替换公开的事件成员变量 事件访问器包含两部分内容:添加访问器和删除访问器.如果涉及公开的事件字段,应该始终使用事件访问器.代码如下所示: class SampleClass ...

  10. 编写高质量代码改善C#程序的157个建议——建议150:使用匿名方法、Lambda表达式代替方法

    建议150:使用匿名方法.Lambda表达式代替方法 方法体如果过小(如小于3行),专门为此定义一个方法就会显得过于繁琐.比如: static void SampeMethod() { List< ...

随机推荐

  1. 关于ListView和GridView的应用

    这两篇博文分别讲的很好: ListView: http://www.cnblogs.com/noTice520/archive/2011/12/05/2276379.html GridViw: htt ...

  2. 解决spring 事务管理默认不支持SQLException等运行时异常

    公司同事在定位一个bug时,发现spring默认的事务只支持运行时异常的回滚,对于像SQLException这样的非运行时异常,默认的事务机制不能处理,于是找了下解决的办法:    1.在捕获SQLE ...

  3. 数据流重定向和管道命令, grep, tr,sort, wc, cut,split,tee,sleep(shell 02)

    主要内容 1.标准输入输出和错误 2.管道命令和 grep, tr,sort, wc, cut,split,tee,sleep 标准输入输出和错误 标准输入(stdin) 是指令数据的输入,代码为0, ...

  4. php设计模式之单例(多例),注册器,观察者模式

    单例(Singleton)模式和不常见的多例(Multiton)模式控制着应用程序中类的数量.如模式名称,单例只能实例化一次,只有一个对象,多例模式可以多次实例化. 基于Singleton的特性,我们 ...

  5. 数据科学:numpy.where() 的用法

    原文出处:numpy.where() 用法讲解 原创作者:massquantity numpy.where() 有两种用法: 1. np.where(condition, x, y) 满足条件(con ...

  6. java代码求输入数的平均值~~~~

    总结:1.谢谢程老师,一个很好的老师,人很普通,但是浑浊的世界里,那一份真实感动到底~~~~很感谢他 2.这里注意两个方面,也是我最大的弱点:循环和数组的length属性.前者运用不灵活,后者自己总是 ...

  7. mysql实战优化之三:表优化

    对于大多数的数据库引擎来说,硬盘操作可能是最重大的瓶颈.所以,把你的数据变得紧凑会对这种情况非常有帮助,因为这减少了对硬盘的访问. 如果一个表只会有几列罢了(比如说字典表,配置表),那么,我们就没有理 ...

  8. Mongodb3.0 新增用户身份验证db.createUser()

    定义:创建一个数据库新用户用db.createUser()方法,如果用户存在则返回一个用户重复错误. 语法:db.createUser(user, writeConcern)    user这个文档创 ...

  9. AngularJS:控制器

    ylbtech-AngularJS:控制器 1.返回顶部 1. AngularJS 控制器 AngularJS 控制器 控制 AngularJS 应用程序的数据. AngularJS 控制器是常规的  ...

  10. python签名设计

    将一个签名网站http://www.uustv.com/的内容爬下来显示出来 代码:sign.py from tkinter import * from tkinter import messageb ...