Java數據結構和算法(1) 自定義一個數組類和動態數組類

####前言 今天就要離校了,大學生涯也走到了盡頭。確定有不少不捨,不捨的是學校的安逸和美麗的女朋友。同時也對本身的將來充滿着信心,但願本身可以強大起來,保護本身想要保護的人。以前一段時間,在掘金上面看到一篇文章,文章提到了一個思想:學會編程,而不是學會Java,文中提到了自定義一個模仿ArrayList的類,要去實現其中的add,get,remove等方法。同時正好我以前也在看《Java數據結構和算法》這本書,文中第二章也詳細講解了數組,因此本身也動手完成了自定義一個數組內和動態數組類,因而乎就有了這篇文章去溫故而知新。程序員


####數組 數組是應用最普遍的數組存儲結構。 優勢:插入快,若是知道下標,能夠很是地存取 缺點:查找慢,刪除慢,大小固定。算法


####動態數組 Java也提供了順序結構的動態數組類ArrayList,數組採用的是順序結構來存儲數據,能夠有效利用空間,可用於存儲大量的數據,數組不適合動態的改變它所存儲的數據,如增長,刪除一個單元等。因爲數組採用順序結構存儲數據,數組得到第n單元中的數據的速度要比鏈表得到第n單元中的數據快。編程

####寫一個數組類 這個數組類確定有insert(),find(),delete(),display()這些基礎方法。 insert():插入一個元素,而後數組長度+1,返回true。數組

public void insert(long value){
		a[count]=value;
		count++;
	}
複製代碼

find():根據元素的值,遍歷整個數組的元素,找到返回true,沒有找到返回falsebash

public boolean find(long searchValue){
		int j;
		
		for(j=0;j<count;j++){
			
			if(a[j]==searchValue){
				break;
			}
		}
		
		if(j==count){
			return false;
		}else{
			return true;
		}
	}
複製代碼

delete():根據元素的值,遍歷整個數組的元素,沒有找到對應的元素值,返回flase, 找到了該元素存儲在數組的位置,讓該位置後面的全部元素向前移動一個位置,返回true。數據結構

public boolean delete(long deleteValue){
		
		int j;
		for(j=0;j<count;j++){
			
			if(a[j]==deleteValue){
				
				break;
			}
			
		}
		
		if(j==count){
			
			return false;
		}else{
			
			for(int k=j;k<count;k++){
				
				a[k]=a[k+1];
			}
			
			count--;
			return true;
		}
	}
	
複製代碼

display():遍歷整個數組的元素,打印數組。app

public void display(){
		int j;
		for(j=0;j<count;j++){
			System.out.print(a[j]+" ");
		}
		System.out.print("\n");
		
	}
複製代碼

####寫一個簡單的動態數組類 其實這仍是比較難的,可是看了一下ArrayList的源碼後,寫一個帶有基本功能的類仍是沒有問題的。基本方法:add(),remove(),contain(),toString(),toArray(),removeRange(),get()等。數據結構和算法

首先是構造器,有2個構造器,分別一個是有參和無參的。有參的構造器須要傳入的參數是所需初始化數組的容量大小,若是這個容量大小>0,那麼建立一個數組,數組容量大小爲傳入的參數。若是這個容量大小=0,那麼把EMPTY_ELEMENTDATA這個空數組對象賦給elementData這個數組變量。若是這個容器大小<0,那麼拋出參數異常。對於無參構造器而言,直接把elementData數組變量引用這個DEFAULT_CAPACITY_ELEMENTDATA數組對象。ide

public CmazList1(int inititalCapcacity){
		
		if(inititalCapcacity>0){
			
			this.elementData=new Object[inititalCapcacity];
			
		}else if(inititalCapcacity==0){
			this.elementData=EMPTY_ELEMENTDATA;
		}else{
			
			throw new IllegalArgumentException("參數錯誤");
		}
	}
	
	public CmazList1(){
		
		this.elementData=DEFAULT_CAPACITY_ELEMENTDATA;
	}
複製代碼

**contain():**調用indexOf()方法,若是indexOf返回的int值大於0就說明此對象在數組中找到對應存儲的位置,那麼返回true,不然返回false。ui

public boolean contain(Object object){
		
		return indexOf(object)>=0;
	}
複製代碼

**indexOf():**經過遍歷整個數組元素,判斷該數組的某個單元是否包含這個對象,若是找到了這個對象,返回其存儲在數組的下標,不然返回-1。

public int indexOf(Object object){
		
		if(object==null){
			
			for(int i=0;i<size;i++){
				
				if(elementData[i]==null){
					
					return i;
				}
			}
		}else{
			
			for(int i=0;i<size;i++){
				
				if(elementData[i].equals(object)){
					
					return i;
					
				}
			}
		}
		
		return -1;
	}
複製代碼

add():先調用 ensureCapacityInternal()方法,進行相關擴容等處理操做。而後添加元素,size加1。size用於記錄在數組中元素的個數。

public  boolean  add(Object object){
		
		ensureCapacityInternal(size+1);
		
		elementData[size++]=object;
		
		return true;
	}
複製代碼

ensureCapacityInternal():首先看整個英文的意思是確保內部容量。首先要判斷這個數組是哪個構造器初始化的。若是這個數組是無參構造器初始化的,那麼這個數組確定沒有設置初始化數組的容量大小,是一個空數組。而後把minCapacity的值在傳入的minCapacity的值和默認容器大小的值中取出最大的一個值,即爲minCapacity的值。而後調用ensureExplicitCapacity()

private void ensureCapacityInternal(int minCapacity){
		
		
		if(this.elementData==DEFAULT_CAPACITY_ELEMENTDATA){
			
			minCapacity=Math.max(minCapacity, DEFAULT_CAPACITY);
		}
		
		ensureExplicitCapacity(minCapacity);
	}
複製代碼

ensureExplicitCapacity():這個方法進行的操做,去判斷是否要進行擴容。若是minCapacity大於數組的長度,說明這個數組須要進行擴容了,由於數組的空間容不下即將添加的元素。接下來調用grow()去進行擴容。

private void ensureExplicitCapacity(int minCapacity){
		
		modCount++;
		
		if(minCapacity-elementData.length>0){
			
			grow(minCapacity);
		}
	}
複製代碼

**grow():進行擴容的操做,擴容後的大小是原來大小的1.5倍。>>1是右移1位,移動後的值是移動前的值的0.5倍。若是擴容後的大小比minCapacity**小,那麼就把minCapacity的值賦給newCapacity。若是newCapacity的值比咱們以前定義的MAX_ARRAY_SIZE還要大的話,那麼就調用hugeCapacity()方法。MAX_ARRAY_SIZE咱們定義的大小是Integer.MAX_VALUE-8,也就是Integer型數值的最大值-8。補充:**原碼是直接將一個數值換算成二進制數,但計算機以補碼的形式保存全部的整數。**通常狀況下,不會出現newCapacity>MAX_ARRAY_SIZE的狀況。

private void grow(int minCapacity){
		
		int oldCapacity=elementData.length;
		
		int newCapacity=oldCapacity+(oldCapacity>>1);
		
		if(minCapacity-newCapacity>0){
			
			newCapacity=minCapacity;
		}
		
		if(newCapacity-MAX_ARRAY_SIZE>0){
			
			newCapacity=hugeCapacity(minCapacity);
		}
		
		this.elementData=Arrays.copyOf(elementData,newCapacity);
	}
複製代碼

**remove():**遍歷整個數組元素,若是發現該數組包含這個對象,那麼調用fastRemove()刪除存儲此對象的單元。

public boolean  remove(Object object){
		
		if(object==null){
			
			for(int i=0;i<size;i++){
				
				if(elementData[i]==null){
					
					fastRemove(i);
					return true;
				}
			}
		}else{
			
			for(int i=0;i<size;i++){
				
				if(elementData[i].equals(object)){
					fastRemove(i);		
					return true;
				}
			}
		}
		
		return false;
		
	}
複製代碼

**remove():**首先調用get()方法,經過下標獲得此單元存儲的元素值。這個方法返回就是這個元素值。計算出刪除此元素須要挪動的元素個數。若是挪動的元素個數>0,那麼調用System.arraycopy()方法,獲得一個移動後的數組。elementData[--size]=null,移動後的數組剩餘一個存儲沒有任何元素的單元,那麼size--,把沒有存儲任何元素的單元置爲null,通知GC清除無用的內存。 講解:System.arraycopy(src, srcPos, dest, destPos, length) src:源數組 srcPos:源數組要複製元素的起始位置 dest:目的數組 destPos:目的數組把複製的元素放置的起始位置 length:複製元素的長度

public E remove(int index){
		rangeCheck(index);
		modCount++;
		E oldValue=get(index);
		int movedNum=size-1-index;
		
		if(movedNum>0){
			
			System.arraycopy(elementData,index+1, elementData, index, movedNum);
		}
		
		elementData[--size]=null;
		
		return oldValue;
	}
複製代碼

removeRange():須要注意的是toIndex指的是要刪除的最後一個元素的下一個元素的下標。思路和remove()是同樣的,只是remove()是刪除一個元素,removeRange()刪除的是多個元素。

public void removeRange(int fromIndex,int toIndex){
		
		modCount++;
		
//		int movedNum=size-1-(toIndex-1);
		int movedNum=size-toIndex;
		
		if(movedNum>0){
			
			System.arraycopy(elementData, toIndex, elementData, fromIndex, movedNum);
		}

		int newSize=size+fromIndex-toIndex;
		
		for(int i=newSize;i<size;i++){
			
			elementData[newSize]=null;
		}
		
		size=newSize;
		
	}
複製代碼

源碼附上

public class CmazList1<E> {
	
	private static final Object[] DEFAULT_CAPACITY_ELEMENTDATA={};
	
	private static final Object[] EMPTY_ELEMENTDATA={};
	
	private static final int MAX_ARRAY_SIZE=Integer.MAX_VALUE-8;
	
	private static int DEFAULT_CAPACITY=10;
	
	private Object[] elementData;
	
	private int modCount=0;
	
	private int size;
	
	public CmazList1(int inititalCapcacity){
		
		if(inititalCapcacity>0){
			
			this.elementData=new Object[inititalCapcacity];
			
		}else if(inititalCapcacity==0){
			this.elementData=EMPTY_ELEMENTDATA;
		}else{
			
			throw new IllegalArgumentException("參數錯誤");
		}
	}
	
	public CmazList1(){
		this.elementData=DEFAULT_CAPACITY_ELEMENTDATA;
	}
	
	public boolean contain(Object object){
		
		return indexOf(object)>=0;
	}
	
	public int indexOf(Object object){
		
		if(object==null){
			
			for(int i=0;i<size;i++){
				
				if(elementData[i]==null){
					
					return i;
				}
			}
		}else{
			
			for(int i=0;i<size;i++){
				
				if(elementData[i].equals(object)){
					
					return i;
					
				}
			}
		}
		
		return -1;
	}
	
	public  boolean  add(Object object){
		
		ensureCapacityInternal(size+1);
		
		elementData[size++]=object;
		
		return true;
	}
	
	private void ensureCapacityInternal(int minCapacity){
		
		
		if(this.elementData==DEFAULT_CAPACITY_ELEMENTDATA){
			
			minCapacity=Math.max(minCapacity, DEFAULT_CAPACITY);
		}
		
		ensureExplicitCapacity(minCapacity);
	}
	
	
	private void ensureExplicitCapacity(int minCapacity){
		
		modCount++;
		
		if(minCapacity-elementData.length>0){
			
			grow(minCapacity);
		}
	}
	
	private void grow(int minCapacity){
		
		int oldCapacity=elementData.length;
		
		int newCapacity=oldCapacity+(oldCapacity>>1);
		
		if(minCapacity-newCapacity>0){
			
			newCapacity=minCapacity;
		}
		
		if(newCapacity-MAX_ARRAY_SIZE>0){
			
			newCapacity=hugeCapacity(minCapacity);
		}
		
		this.elementData=Arrays.copyOf(elementData,newCapacity);
	}
	
	private int hugeCapacity(int minCapacity){
		
		if(minCapacity<0){
			
			throw new OutOfMemoryError();
		}
		return minCapacity>MAX_ARRAY_SIZE?Integer.MAX_VALUE:MAX_ARRAY_SIZE;
	}
	
	public boolean  remove(Object object){
		
		if(object==null){
			
			for(int i=0;i<size;i++){
				
				if(elementData[i]==null){
					
					fastRemove(i);
					return true;
				}
			}
		}else{
			
			for(int i=0;i<size;i++){
				
				if(elementData[i].equals(object)){
					fastRemove(i);		
					return true;
				}
			}
		}
		
		return false;
		
	}
	
	
	private  void fastRemove(int index){
		modCount++;
		int movedNum=size-1-index;
		if(movedNum>0){
		
			System.arraycopy(elementData, index+1, elementData, index, movedNum);
		}
		
		elementData[--size]=null;
		
	}
	
	public E remove(int index){
		rangeCheck(index);
		modCount++;
		E oldValue=get(index);
		int movedNum=size-1-index;
		
		if(movedNum>0){
			
			System.arraycopy(elementData,index+1, elementData, index, movedNum);
		}
		
		elementData[--size]=null;
		
		return oldValue;
	}
	
	public E get(int index){
		rangeCheck(index);
		return (E) elementData[index];
	}

	@Override
	public String toString() {
		// TODO Auto-generated method stub
		
		StringBuilder sb=new StringBuilder();
		for(int i=0;i<size;i++){
			sb.append(elementData[i]+" ");
		}
		return sb.toString();
	}
	
	public Object[] toArray(){
		
		return Arrays.copyOf(this.elementData, size);
	}
	
	/**
	 * toIndex是指要刪除的最後一個元素的下一個元素的下標
	 * 好比我要刪除下標爲5的元素,那麼toIndex=6
	 * @param fromIndex
	 * @param toIndex
	 */
	public void removeRange(int fromIndex,int toIndex){
		
		modCount++;
		
//		int movedNum=size-1-(toIndex-1);
		int movedNum=size-toIndex;
		
		if(movedNum>0){
			
			System.arraycopy(elementData, toIndex, elementData, fromIndex, movedNum);
		}
//		
		int newSize=size+fromIndex-toIndex;
		
		for(int i=newSize;i<size;i++){
			
			elementData[newSize]=null;
		}
		
		size=newSize;
		
	}
	
	private  void rangeCheck(int index){
		
		if(index>=size||index<0){
			
			throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
		}
	}
	
	
	private String outOfBoundsMsg(int index){
		return "index:"+index+",size="+size;
	}
}
複製代碼

####尾言 數據結構和算法的複利性遠比其餘時髦技術要多的多。對於程序員來講,不進則死。 打好牢固的基礎,必定是沒有錯的。笨鳥先飛,很適合我。

相關文章
相關標籤/搜索