這段時間在復習舊有的基礎知識,看到了泛型,裝箱,拆箱等操作。然后回憶起多年前一位面試官問起的一個問題,“你覺得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中使用泛型沒有得到效率的提升,但無論在編程的習慣上,還是提高代碼的質量上都提倡使用泛型。
