1. 程式人生 > >【第13天】Java集合(二)---手動實現ArrayList及其他List介面實現的集合

【第13天】Java集合(二)---手動實現ArrayList及其他List介面實現的集合

1 ArrayList(續)

1.1 擴容與縮容

ArrayList list = new ArrayList(50);//開闢一個長度為50的集合
ArrayList list = new ArrayList();//開闢一個預設大小的集合,預設值底層設定為10

不管開闢一個定長還是預設長度的集合,其實都可以裝無數個元素,集合可以自動地擴容。

  • 擴容的倍數:
    JDK6.0及以前:x * 3/2 + 1(整數型別,去除結果的小數部分)
    JDK7.0及以後:x + (x >> 1) (—> x * 3/2)

  • 在實際開發時要避免擴容,因為擴容的底層其實是建立一個更大的陣列將老陣列替換:

    1. 建立一個更大的陣列物件
    2. 將老數組裡面的元素賦值到新數組裡面
    3. 改變引用指向
    4. 回收老陣列物件
    5. 繼續新增元素
  • 如何手動擴/縮容?

    • 擴容:list.ensureCapacity(int 容量);
    • 縮容:list.trimToSize();//去掉因擴容倍數而產生的空餘集合空間,將空間大小縮小為元素個數。
  • 雖然陣列自動擴容,但是考慮到效率開發,最好定一個大的空間,避免因自動擴容造成效率降低。

  • 手動擴容比自動擴容效率更高。

1.2 手動實現ArrayList

  • 要手動實現(Customed)ArrayList類需建立的成員(2個成員變數/9個方法):

    • 私有的集合的陣列底層實現,用來裝元素:private Object[] data;

    • 私有表示集合大小,元素個數:private int size;

    • 有參構造方法接收指定長度建立的集合:public EtoakList(int x){}

    • 無參構造方法接收預設

      長度建立的集合,預設開闢10塊空間:public EtoakList(){}

    • 獲取當前集合裡元素個數,因不需要setSize(),所以getSize()簡化為size():public int size(){}

    • 獲取指定下標元素:public Object get(int x){}

    • 新增元素到集合:public void add(Object obj){}

    • 按下標刪除元素:public void remove(int x){}

    • 按元素所屬類指定的equals()刪除obj這樣的元素:public void remove(Object obj){}

    • 按元素所屬類指定的equals()判斷是否存在obj這樣的元素:public boolean contains(Object obj){}

    • 重寫Object類中的toString()

//無泛型版本(JDK6.0之前)
public class TestCustomedArrayList1{
	public static void main(String[] args){
		
		//測試
		CustomedArrayList1 caList = new CustomedArrayList1();
		caList.add(32);
		caList.add(40);
		caList.add(6);

		System.out.println(caList.size());//--->2
		System.out.println(caList.get(0));//--->45
		System.out.println(caList.contains(6));//--->true

		caList.remove(new Integer(40));
		System.out.println(caList.size());//--->2

		System.out.println(caList);//[32,6]
	}
}

class CustomedArrayList1{

	//集合的陣列底層實現,用來裝元素
	//為了保證什麼資料型別都可以裝,所以使用Object[]
	private Object[] data;

	//表示集合大小,元素個數
	private int size;


	//接收指定長度建立的集合
	//CustomedArrayList1 list = new CustomedArrayList1(-45);
	public CustomedArrayList1(int x){//x : 陣列空間大小
		if(x < 0){//負數
			System.out.println("引數異常");
		}else{//x >= 0
			//定義新陣列,即建立集合
			this.data = new Object[x];
		}
	}

	//接收預設長度建立的集合,預設開闢10塊空間
	//CustomedArrayList1 list = new CustomedArrayList1();
	public CustomedArrayList1(){
		//data = new Object[10];
		//直接呼叫有參構造並傳入預設值
		this(10);
	}

	//獲取當前集合裡元素個數,因不需要setSize(),所以getSize()簡化為size()
	//int x = list.size();
	public int size(){
		return size;
	}

	//獲取指定下標元素
	//Object obj = list.get(-6);
	public Object get(int x){//x:下標
		if(0 <= x && x < size){//x --> 下標正常
			return data[x];
		}else{//下標超出邊界
			return "下標超出邊界異常";
		}
	}


	//新增元素到集合
	//list.add(元素);
	public void add(Object obj){

		//向集合中新增元素時,底層其實會把這個元素,置入成員data數組裡面
		//新增元素前需陣列物件是否已滿
		if(data.length == size){
			//如果滿了就擴容
			//建立一個新的陣列物件
			//JDK6.0及之前 x * 3 / 2 + 1
			Object[] temp = new Object[size * 3 / 2 + 1];
			//將老數組裡面的元素賦值到新數組裡
			System.arraycopy(data,0,temp,0,size);
			//改變引用指向,新陣列賦值給老陣列新的地址
			data = temp;
			//gc回收老陣列空指向的物件
			//擴容後再新增元素
			data[size] = obj;
			//變數++
			size++;
		}else{
			//如果沒滿
			//直接新增元素
			data[size] = obj;
			//變數++
			size++;
		}
	}

	//按下標刪除元素
	//list.remove(3);
	public void remove(int x){//x:下標
		//從集合裡刪除下標x對應的元素時
		//就是從數組裡刪除下標x對應的元素

		System.arraycopy(data, x+1, data, x, size-(x+1));
		
		//集合長度減小
		size--;

		/** 類推得arraycopy引數設定:
			list.remove(1);
			45  66  90  15  15
			45	87	66	90	15 -> ArrayList
			[0]	[1]	[2]	[3]	[4]

			刪除下標0	從下標1開始複製4=5-1個元素
			刪除下標1	從下標2開始複製3=5-2個元素
			刪除小標2	從下標3開始複製2=5-3個元素
			刪除下標x	從下標x+1開始複製data.length-(x+1)個元素
		*/
	}


	//按元素所屬類指定的equals()刪除obj這樣的元素
	//list.remove("B");
	public void remove(Object obj){
		//底層使用obj和集合裡每個元素equals()

		for(int x = 0;x < size;x++){
			//x -> 下標
			//data[x] -> 元素
			if(obj.equals(data[x])){
				//下標x對應的元素和obj視為相等物件
				//刪除下標x對應的元素
				//一個remove方法只能刪除一個元素
				remove(x);
				break;
			}
		}
	}

	//按元素所屬類指定的equals()判斷是否存在obj這樣的元素
	//boolean x = list.contains(Object obj)
	public boolean contains(Object obj){
		//底層使用obj和集合裡每個元素equals()

		//list -> 張三 李四 王五
		//list.contains("李四")
		for(int x = 0;x < size;x++){
			//x -> 下標
			//data[x] -> 元素
			if(obj.equals(data[x])){
				return true;
			}
		}
		return false;
	}

	@Override
	public String toString(){
		//[元素,元素,元素]

		String str = "[";
		for(int x = 0;x < size;x++){
			//x -> 下標
			//data[x] -> 元素
			str += data[x];//data[x].toString()
			if(x != size-1){
				str += ",";
			}
		}

		//[元素,元素,元素]
		str += "]";
		return str;
	}
}
//泛型版本(JDK7.0之後) ★
public class TestCustomedArrayList2{
	public static void main(String[] args){

		AList<Student> list = new AList<Student>();
		Student stu1 = new Student("張三");
		Student stu2 = new Student("張三");

		list.add(stu1);
		list.remove(stu2);
		System.out.println(list);//--->[]
	}
}

class Student{
	String name;
	public Student(String name){
		this.name = name;
	}

	@Override
	public String toString(){
		return name;
	}

	@Override
	public boolean equals(Object obj){
		if(!(obj instanceof Student))return false;
		if(obj == null )return false;
		if(obj == this) return true;
		return this.name.equals(((Student)obj).name);
	}
}

class CustomedArrayList2<E>{//ArrayList

	private Object[] data;
	private int size;

	public CustomedArrayList2(int x){
		data = new Object[x];
	}

	public CustomedArrayList2(){
		data = new Object[10];
	}

	public int size(){
		return size;
	}

	public Object get(int x){
		return data[x];
	}

	public void add(E obj){
	
		if(data.length == size){

			Object[] temp = new Object[size + (size>>1)];
			System.arraycopy(data,0,temp,0,size);
			data = temp;
		}
		data[size] = obj;
		size++;

	}

	public void remove(int x){

		System.arraycopy(data,x+1,data,x,size-(x+1));
		size--;
	}

	public void remove(Object obj){

		for(int x = 0;x < size;x++){
			if(obj.equals(data[x])){
				remove(x);
				break;
			}
		}
	}

	public boolean contains(Object obj){

		for(int x = 0;x < size;x++){
			if(obj.equals(data[x])){
				return true;
			}
		}
		return false;
	}
	
	@Override
	public String toString(){

		StringBuffer buffer = new StringBuffer("[");

		for(int x = 0;x < size;x++){
			buffer.append(data[x].toString());
			if(x != size-1){
				buffer.append(",");
			}
		}
		buffer.append("]");
		return buffer.toString();
	}
}

2 Vector

       Vector與ArrayList使用方法(語法)相同,只是在概念上有所不同:

  • 同步執行緒不同
    Vector同一時間允許單個執行緒進行訪問,效率較低,但不會出現併發錯誤;
    ArrayList同一時間允許多個執行緒訪問,效率較高,但可能出現併發錯誤。

JDK5.0開始,集合的工具類(Collections)裡面提供的一個方法(synchronizedList)可以將執行緒不安全的ArrayList物件變成執行緒安全的集合物件,於是Vector漸漸被淘汰。

  • 擴容機制不同
    Vector分構造方法,(Vector(10) -> 2倍擴容 10->20->40->80…;Vector(10,3)->定長擴容 10->13 ->16->19…);
    ArrayList分版本(JDK6.0之前 x * 3 / 2 + 1;JDK7.0之後 x + (x >> 1))。

  • 出現的版本不同
    Vector出現於JDK1.0;
    ArrayList出現於JDK1.2。

3 LinkedList

       LinkedList與ArrayList使用方法(語法)相同,只是在概念上有所不同:

  • ArrayList和LinkedList底層採用的資料結構不同 導致優劣勢不同

    • ArrayList:底層基於陣列實現,
      優點:陣列連續儲存,所以方便查詢遍歷和隨機訪問。
      缺點:新增刪除因為要維護連續(1.建立新陣列物件 2.複製老陣列元素 3.改變引用指向 4.回收老陣列 ),所以增刪效率較低。

    • LinkedList:底層基於連結串列實現,
      優點:連結串列直接通過改變地址指向進行增刪,增刪元素效率高
      缺點:每次遍歷查詢都需要從頭找起,隨機訪問、遍歷查詢效率低

  • 在開發的時候,儘量避免使用LinkedList的get(int 下標)方法

  • ArrayList更適合訪問、讀取資料,LinkedList適合增刪資料

4 Stack

       Stack與ArrayList使用方法(語法)相同,只是在概念上有所不同:這個集合的意義是用陣列模擬棧結構。

import java.util.*;
public class TestStack{
	public static void main(String[] args){

		Stack<Integer> ss = new Stack<>();

		ss.push(666);//從棧頂壓入一個元素
		ss.push(777);//從棧頂壓入一個元素
		ss.push(888);//從棧頂壓入一個元素

		System.out.println(ss.pop());//--->888
		System.out.println(ss.pop());//--->777
		System.out.println(ss.pop());//--->666
	}
}