ArrayList与List的使用区别
这段时间在复习旧有的基础知识,看到了泛型,装箱,拆箱等操作。然后回忆起多年前一位面试官问起的一个问题,“你觉得ArrayList与List的有什么使用上的区别”,当时我还是一个基础知识好薄弱的码农,只知道使用上的不同,完全不知道面试官想要考核的内容深度。
如果有一定基础知道的朋友,一定好快会答得出这个问题的核心思想。就是性能。
或许还会有一些做了很多年的程序员的朋友,仍然弄不清当中意义,正好今天心血来潮,就简单的总结一下。
首先名词解释:
装箱:在值类型向引用类型转换时发生;
拆箱:在引用类型向值类型转换时发生;
值类型:直接将内存存储在栈内,由系统自动释放资源的数据类型;
引用类型:由类型的实际值引用(类似于指针)表示的数据类型,通俗点说就是在编程时需要new出来的变量类型都是引用型,引用类型是存放在内存的堆中;
内存堆跟栈的定义跟数据结构的堆栈是不同的,其实网上也有很多的解释,我在这里就通俗的给大家说一下。
栈:由大至小的分配,先进后出,直接存放值类型的地方;我们一般出现的内存溢出就是由于栈位都分配完了;
堆:由小至大的分配,随意存储,存放引用类型的地方;
C#的值类型(在C#中所有的值类型都继承自:System.ValueType)
整型:Int;
长整型:long;
浮点型:float;
字符型:char;
布尔型:bool;
枚举:enum;
结构:struct;
看了这么多的名词解释,我们再回到最初的话题ArrayList与List的使用区别。
先看ArrayList的使用:
1 public void ArrayListDemo() 2 { 3 ArrayList list = new ArrayList(); 4 list.add(1); 5 list.add(2); 6 7 foreach(int i in list) 8 { 9 Console.WriteLine("value is {0}", value); 10 } 11 }
由于ArrayList的每个item默认是Object的类型,所以当我们执行语句list.add(1);的时候,就是做了一次装箱的操作。同理,在for循环里list的每一项都要做一个拆箱的操作才能得到变量i,最后到打印变量i时,由于字符串也是引用类型,所以也要做一次的装箱的操作。这里前后一共做了6次的装箱拆箱(4次装箱,2次拆箱),每一次的装箱拆箱都涉及CPU以及内存的分配,都是性能的损耗。
由.net2.0开始,就引入了泛型这个很好用的东西,下面看看List的使用:
1 public void ListDemo() 2 { 3 List<int>list = new List<int>(); 4 list.add(1); 5 list.add(2); 6 7 foreach(int i in list) 8 { 9 Console.WriteLine("value is {0}", value); 10 } 11 }
由于List使用了泛型,我们指定了item必需是int类型,所以在add item的时候,不需要再进行装箱拆箱的操作,一直到打印i的时候,才需要做装箱的操作,整段代码执行完以后,一共才进行2次的装箱拆箱(2次装箱,0次拆箱)。
如果List的item多,程序运行时,相对于ArrayList来说就会节省很多的系统资源。所以List与ArrayList的使用区别,到最后就是性能的表现问题,但其中的原理,看完上面的介绍,相信如果有人再问你,你会很容易的回答出来。
最后顺带一提,.net的泛型与Java的泛型是不一样的,虽然都叫泛型,但.net对泛型的类型指定在编译运行时是不会取消的,所以大大减少了类型转换(装箱拆箱)的操作,而Java在编译时泛型的类型指定是移除了,所以即使编码时指定了相关类型,但运行时依然要进行类型转换(装箱拆箱)的操作,性能没有得到提升。虽然如此,泛型除了提高运行性能外,还有其它的用途,例如增加程序的可读性,增加程序的编程安全性等。所以即使在Java中使用泛型没有得到效率的提升,但无论在编程的习惯上,还是提高代码的质量上都提倡使用泛型。