1. 程式人生 > >原始碼分析篇--Java集合操作(1)

原始碼分析篇--Java集合操作(1)

一、集合框架
1、集合框架體系圖
在這裡插入圖片描述
2、集合的概念
 Java集合是使程式能夠儲存和操縱元素不固定的一組資料。 所有Java集合類都位於java.uti包中。與Java陣列不同,Java集合中不能存放基本資料型別,只能存放物件的引用。但是在JDK5.0以後的版本當中,JAVA增加了“自動裝箱”和“自動拆箱”的機制,比如如果要存入一個INT型別的資料,JVM會把資料包裝成Integer然後再存入集合,看起來集合能夠存入基本資料型別,其實是不能的只是多了一個包裝資料的過程。我們來看個例子,驗證一下集合存放的是物件的引用(記憶體地址,什麼是記憶體地址呢?記憶體地址就是計算機中使用16進位制或其他進位制來對映一個字元,這個進位制數就是實際字元在記憶體中所對映的地址,記憶體地址是記憶體當中儲存資料的一個標識,並不是資料本身,通過記憶體地址可以找到記憶體當中儲存的資料。),而不是物件資料本身:

package com.yzh.maven.main;
import java.util.ArrayList;
import java.util.List;
import com.yzh.maven.entity.UserInfo;
public class CollectionTest{
	private static Integer[] arr = null;
	public static void main(String[] args) {
		///////////////////////list元素的新增/////////////////////////
		List<UserInfo> list = new ArrayList<UserInfo>();
		UserInfo u = null;
		for(int i = 0;i<5;i++){
			u = new UserInfo();
			u.setUserName("yzh"+i);
			u.setPassword("123"+i);
			list.add(u);
		}
		for(int i = 0;i<list.size();i++){
			//System.out.println(System.identityHashCode(list.get(i)));
			System.out.println(list.get(i));
		}
		List<Integer> list2 = new ArrayList<Integer>();
		for(int i = 0;i<5;i++){
			list2.add(i);
		}
		System.out.println(list2.get(0).getClass());
	}
}
output:
[email protected]
[email protected] [email protected] [email protected] [email protected] class java.lang.Integer

注意:
① System.out.println(xx),括號裡面的“xx”如果不是String型別的話,就自動呼叫xx的toString()方法。
② Java中列印物件記憶體地址
Object的hashCode()預設是返回記憶體地址的,但是hashCode()可以重寫,所以hashCode()不能代表記憶體地址的不同。System.identityHashCode(Object)方法可以返回物件的記憶體地址,不管該物件的類是否重寫了hashCode()方法。

3、集合與陣列的區別
陣列是最常見的鏈式儲存結構,它是一段連續的記憶體空間,在記憶體中我們可以簡單表示為下圖樣式
在這裡插入圖片描述
注意:連結串列與陣列是兩種不同的資料結構,資料結構可以分為線性結構和非線性結構,線上性結構中,儲存方式又分為連續儲存(陣列)和離散儲存(連結串列),例如棧和佇列都是線性結構常見的應用。我們可以將陣列簡單地理解為一種線性表資料結構(線性表是動態的),因為陣列一旦定義了,其長度就不可以更改了,也就是不可以做增刪操作,但可以做修改和查詢操作。不存在什麼先進先出的含義。
用陣列存放一堆相同型別物件也是一個不錯的選擇,但是有一個很大的缺陷,那就是陣列大小隻能是固定的,不能從數組裡動態新增和刪除一個物件,要擴容的時候,就只能新建一個數組然後把原來的物件全部複製到新的數組裡,而且只能存放相同型別的物件,使用起來不夠靈活。下面我們來使用陣列實現擴容,如下所示。

package com.yzh.maven.main;
public class CollectionTest3 {
	//初始化元素陣列
	private static Object[] src = new Object[1];
	private static int len = src.length; 
	//目標陣列
	private static Object[] to = new Object[len];
	//記錄新增元素的個數
	private static int countTemp = 0;
	//中間陣列
	private static Object[] obj = new Object[len];
	public static void main(String[] args) {
		for(int i = 0;i<12;i++){
			add("AA"+i);
		}

		for(Object o:to){
			System.out.print(o+" ");
		}
		
		System.out.println("\n原素個數"+size());
	}
	
	public static void add(Object i){
		int temp = src.length - 1;
		//最後一個元素不為空,說明陣列元素已經滿了,就擴容
		if(src[temp] != null){
			//在擴充套件前需要儲存to陣列的資料
			System.arraycopy(to, 0, obj, 0,len);
			//擴容
			len = len+1;
			//動態擴充套件陣列,增加陣列容量
			to = new Object[len];
//將obj陣列從0開始的元素拷貝to陣列到從0-第len-1個元素的位置,從obj中間陣列中獲取資料到to陣列
			System.arraycopy(obj, 0, to, 0,len-1);
			//新增元素
			to[len-1]=i;
			//克隆陣列是為了每次使長度增1
			obj = to.clone();
		      //如果是第一個元素(陣列元素未滿),也就是最後一個元素為空
		}else{
			src[0] = i;
			System.arraycopy(src, 0, to, 0, countTemp+1);
		}
		countTemp++;
		return;
	}
	
	public static int size(){
		return countTemp;
	}
}
/**output:
AA0 AA1 AA2 AA3 AA4 AA5 AA6 AA7 AA8 AA9 AA10 AA11 
12
*/

實際上,集合的底層是一個動態陣列,原理類似於上面的情形。可以判斷的是,在List集合的底層方法中,一定使用了arraycopy()方法來實現動態陣列擴容。陣列擴容的大概思路是,先判斷陣列的最後一個元素是否有值,如果有值,說明陣列溢滿,陣列需要擴容才能新增元素了;如果沒值,說明陣列沒滿,就可以繼續新增元素。在擴容時,需要定義一箇中間陣列來儲存未擴容的目標陣列的資料,否則會丟失記憶體中原有的資料,而且需要將中間陣列的長度也要進行擴容,例如obj = to.clone();只是為了擴充套件中間陣列容量而已,還可以將其改寫成obj = new Object[len];來達到擴充套件中間陣列容量。使用陣列進行容量擴充套件是一件很麻煩的事,但是,我們使用已經封裝好的List的實現類就不一樣了:

package com.yzh.maven.main;

import java.util.ArrayList;
import java.util.List;
public class CollectionTest5  {
	public static void main(String[] args) {
		List<String> list = new ArrayList<String>();
		for(int i = 0;i<12;i++){
			list.add("AA"+i);
		}

		for(Object o:list){
			System.out.print(o+" ");
		}
	}
}
/**output:
AA0 AA1 AA2 AA3 AA4 AA5 AA6 AA7 AA8 AA9 AA10 AA11 
*/

我們看到,實現的效果和上面的一樣,但是相比陣列的操作就簡單多了。