Java使用數(shù)組實(shí)現(xiàn)ArrayList的動(dòng)態(tài)擴(kuò)容的方法
提到數(shù)組大家肯定不會(huì)陌生,但我們也知道數(shù)組有個(gè)缺點(diǎn)就是在創(chuàng)建時(shí)就確定了長(zhǎng)度,之后就不能更改長(zhǎng)度。所以Java官方向我們提供了ArrayList這個(gè)可變長(zhǎng)的容器。其實(shí)ArrayList底層也是用數(shù)組進(jìn)行實(shí)現(xiàn)的,今天我們就自己使用數(shù)組實(shí)現(xiàn)ArrayList的功能。
一、整體框架
廢話不多說,我們以存放int類型元素為例,看一下ArrayList需要的成員變量和需要實(shí)現(xiàn)的方法。
public class ArrayList private int size;//用來記錄實(shí)際存儲(chǔ)元素個(gè)數(shù) private int[] elements;//用來存儲(chǔ)元素的數(shù)組 //構(gòu)造方法:在創(chuàng)建ArrayList對(duì)象時(shí),初始化elements數(shù)組 public ArrayList(int capacity){ elements = new int[capacity]; } /** * 元素的數(shù)量 * @return */ public int size() {} /** * 是否為空 * @return */ public boolean isEmpty() {} /** * 查看元素的索引 * @param element * @return */ public int indexOf(int element) {} /** * 是否包含元素 * @param element * @return */ public boolean contains(int element) {} /** * 獲取index位置的元素 * @param index * @return */ public int get(int index) {} /** * 設(shè)置index位置的元素 * @param index * @param element * @return 原來的元素 */ public int set(int index, int element) {} /** * 在index索引位置插入元素 * @param index * @param element */ public void add(int index, int element) {} /** * 添加元素到尾部 * @param element */ public void add(int element) {} /** * 刪除index位置的元素 * @param index * @return */ public int remove(int index) {} /** * 清除所有元素 */ public void clear() {} /** * 用來打印列表 */ public String toString() {} }
二、方法實(shí)現(xiàn)
框架我們已經(jīng)有了,接下來我們一步步實(shí)現(xiàn)方法就行。
size()方法:
這個(gè)方法很簡(jiǎn)單,因?yàn)槲覀冇衧ize屬性,直接將其返回就行了。
public int size() { return size;}
isEmpty()方法:
這個(gè)方法也很簡(jiǎn)單,判斷是否為空只需要判斷size是否為0即可。
public boolean isEmpty() { return size == 0;}
indexOf(int element)方法:
這個(gè)方法是用來查詢?cè)氐乃谒饕恢?,并返回。我們通過遍歷列表查找即可,找到就將元素返回,沒有找到返回-1。
public int indexOf(int element) { for (int i = 0; i < size; i++) { if (element.equals(elements[i])) { return i; } return -1; }
contains(int element)方法:
這個(gè)方法是用來查看所傳元素是否在數(shù)組中,我們可以直接通過indexOf()方法查看返回值是否不等于-1,不等于-1返回true,等于-1返回false。
public boolean contains(int element) { return indexOf(element) != -1;}
get(int index)方法:
這個(gè)方法用來獲取指定索引位置的元素,我們直接返回?cái)?shù)組的指定索引位置元素即可。
public int get(int index) {; return elements[index];}
set(int index, int element)方法:
這個(gè)方法用來將指定索引位置元素改為指定元素,并將原來元素返回,我們通過數(shù)組索引獲取到該位置元素,并將指定元素賦值給該位置索引并將原來元素返回。
public int set(int index, int element) { int old = elements[index]; elements[index] = element; return old;}
add(int index, int element)方法:
個(gè)方法是在指定索引位置插入指定元素,我們首先將指定索引位置的元素以及之后所有的元素向后移動(dòng)一位,然后再將指定元素賦值給數(shù)組的指定索引位置,最后并將size加1。
public void add(int index, int element) { for (int i = size; i > index; i--) { elements[i] = elements[i - 1]; } elements[index] = element; size++; }
add(int element)方法:
這個(gè)方法是在數(shù)組尾部添加元素,我們直接調(diào)用add(int index, int element)方法,將size作為index的參數(shù)傳入即可。
public void add(int element) { add(size, element);}
remove(int index)方法:
這個(gè)方法用來刪除指定索引位置的元素,并將要?jiǎng)h除元素返回,我們首先通過指定索引獲取原來元素,當(dāng)刪除該元素后需要將index索引后面的所有元素向前移動(dòng)一位,最后將size減1。
public int remove(int index) { int old = elements[index]; for (int i = index + 1; i < size; i++) { elements[i - 1] = elements[i]; } size--; return old;}
clear()方法:
這個(gè)方法,用于清空數(shù)組中所有元素,我們只需要將size賦值為0即可。
public void clear() { size = 0;}
toString()方法:
這個(gè)方法用于將所有元素打印出來,通過StringBuilder對(duì)象進(jìn)行拼接,最后轉(zhuǎn)成字符串返回即可。
public String toString() { StringBuilder string = new StringBuilder(); string.append('size=').append(size).append(', ['); for (int i = 0; i < size; i++) { if (i != 0) { string.append(', '); } string.append(elements[i]); } string.append(']'); return string.toString();}
以上代碼基本實(shí)現(xiàn)了對(duì)數(shù)組的進(jìn)行數(shù)據(jù)的增刪改查,但最后想達(dá)到我們的目的還要進(jìn)一步優(yōu)化。
三、優(yōu)化
1.實(shí)現(xiàn)動(dòng)態(tài)擴(kuò)容
當(dāng)我們向數(shù)組中添加元素時(shí),如果數(shù)組已經(jīng)滿了我們就需要就數(shù)組進(jìn)行動(dòng)態(tài)擴(kuò)容。擴(kuò)容的原理并不是真的對(duì)原數(shù)組進(jìn)行增加內(nèi)存操作,而是重新創(chuàng)建一個(gè)更大的數(shù)組,并將原數(shù)組的元素賦給新的數(shù)組。我們聲明一個(gè)私有方法確保添加元素前數(shù)組的容量足夠。
private void ensureCapacity(int capacity) { int oldCapacity = elements.length; if (oldCapacity >= capacity) return; // 新容量為舊容量的1.5倍 int newCapacity = oldCapacity + (oldCapacity >> 1); int[] newElements = new int[newCapacity]; for (int i = 0; i < size; i++) { newElements[i] = elements[i]; } elements = newElements;}
我們?cè)赼dd()方法中首先調(diào)用ensureCapacity(int capacity)方法進(jìn)行判斷數(shù)組容量是否足夠,不夠進(jìn)行擴(kuò)容。
public void add(int index, E element) { ensureCapacity(size + 1); for (int i = size; i > index; i--) { elements[i] = elements[i - 1]; } elements[index] = element; size++;}
2. 確保索引不越界
當(dāng)我們?cè)谡{(diào)用傳入索引的方法時(shí),首先要保證傳入索引在可用范圍內(nèi),不然會(huì)出現(xiàn)角標(biāo)越界的異常。定義outOfBound()方法,用于拋出角標(biāo)越界異常信息。
private void outOfBounds(int index) { throw new IndexOutOfBoundsException('Index:' + index + ', Size:' + size);}
定義rangeCheck()方法用于檢查索引是否越界,如果越界,調(diào)用outOfBound()拋出異常。
private void rangeCheck(int index) { if (index < 0 || index >= size) { outOfBounds(index); }}
而對(duì)于調(diào)用add()方法時(shí)所傳入的索引范圍可以取到size位置,我們?cè)诙x一個(gè)rangeCheckForAdd()方法,用于檢查調(diào)用add()方法時(shí)索引是否越界。
private void rangeCheckForAdd(int index) { if (index < 0 || index > size) { outOfBounds(index); }}
在get()方法,set()方法和remove()方法中,先調(diào)用rangeCheck()方法,檢查傳入索引是否越界。
public int get(int index) { rangeCheck(index); return elements[index];}public int set(int index, E element) { rangeCheck(index); int old = elements[index]; elements[index] = element; return old;}public int remove(int index) { rangeCheck(index); int old = elements[index]; for (int i = index + 1; i < size; i++) { elements[i - 1] = elements[i]; } size--; return old;}
3. 設(shè)置常量以及重寫構(gòu)造方法
對(duì)于類中的常數(shù)我們更希望封裝成常量,并且聲明一個(gè)默認(rèn)容量,希望在創(chuàng)建對(duì)象時(shí)未傳入容量參數(shù)或傳入容量參數(shù)過小直接創(chuàng)建一個(gè)相對(duì)大小合適的數(shù)組。
private static final int DEFAULT_CAPACITY = 10;//我們將默認(rèn)容量設(shè)為10private static final int ELEMENT_NOT_FOUND = -1;//我們將獲取指定元素的索引時(shí),未找到的返回值-1封裝為常量//聲明無參構(gòu)造方法,在調(diào)用無參構(gòu)造方法是直接創(chuàng)建默認(rèn)容量的數(shù)組public ArrayList(){ elements = new int[DEFAULT_CAPACITY];}//聲明有參構(gòu)造方法,在傳入?yún)?shù)小于默認(rèn)容量時(shí),我們?nèi)匀粍?chuàng)建默認(rèn)容量數(shù)組public ArrayList(int capaticy) { capaticy = (capaticy < DEFAULT_CAPACITY) ? DEFAULT_CAPACITY : capaticy; elements = new int[capaticy];}
四、使用泛型
以上步驟已經(jīng)使用數(shù)組完全實(shí)現(xiàn)了ArrayList的全部功能,但只能存放int類型的數(shù)據(jù),如果我們希望儲(chǔ)存其他類型的數(shù)據(jù)我們只需要使用Java中的泛型就能夠完成。
思路與上述完全一樣,整體代碼如下:
public class ArrayList<E> { /** * 元素的數(shù)量 */ private int size; /** * 所有的元素 */ private E[] elements; private static final int DEFAULT_CAPACITY = 10; private static final int ELEMENT_NOT_FOUND = -1; public ArrayList() { elements = (E[]) new Object[DEFAULT_CAPACITY]; } public ArrayList(int capaticy) { capaticy = (capaticy < DEFAULT_CAPACITY) ? DEFAULT_CAPACITY : capaticy; elements = (E[]) new Object[capaticy]; } /** * 清除所有元素 */ public void clear() { for (int i = 0; i < size; i++) { elements[i] = null; } size = 0; } /** * 元素的數(shù)量 * @return */ public int size() { return size; } /** * 是否為空 * @return */ public boolean isEmpty() { return size == 0; } /** * 是否包含某個(gè)元素 * @param element * @return */ public boolean contains(E element) { return indexOf(element) != ELEMENT_NOT_FOUND; } /** * 添加元素到尾部 * @param element */ public void add(E element) { add(size, element); } /** * 獲取index位置的元素 * @param index * @return */ public E get(int index) { rangeCheck(index); return elements[index]; } /** * 設(shè)置index位置的元素 * @param index * @param element * @return 原來的元素ֵ */ public E set(int index, E element) { rangeCheck(index); E old = elements[index]; elements[index] = element; return old; } /** * 在index位置插入一個(gè)元素 * @param index * @param element */ public void add(int index, E element) { rangeCheckForAdd(index); ensureCapacity(size + 1); for (int i = size; i > index; i--) { elements[i] = elements[i - 1]; } elements[index] = element; size++; } /** * 刪除index位置的元素 * @param index * @return */ public E remove(int index) { rangeCheck(index); E old = elements[index]; for (int i = index + 1; i < size; i++) { elements[i - 1] = elements[i]; } elements[--size] = null; return old; } /** * 查看元素的索引 * @param element * @return */ public int indexOf(E element) { if (element == null) { for (int i = 0; i < size; i++) { if (elements[i] == null) return i; } } else { for (int i = 0; i < size; i++) { if (element.equals(elements[i])) return i; } } return ELEMENT_NOT_FOUND; } /** * 保證要有capacity的容量 * @param capacity */ private void ensureCapacity(int capacity) { int oldCapacity = elements.length; if (oldCapacity >= capacity) return; // 新容量為舊容量的1.5倍 int newCapacity = oldCapacity + (oldCapacity >> 1); E[] newElements = (E[]) new Object[newCapacity]; for (int i = 0; i < size; i++) { newElements[i] = elements[i]; } elements = newElements; } private void outOfBounds(int index) { throw new IndexOutOfBoundsException('Index:' + index + ', Size:' + size); } private void rangeCheck(int index) { if (index < 0 || index >= size) { outOfBounds(index); } } private void rangeCheckForAdd(int index) { if (index < 0 || index > size) { outOfBounds(index); } } @Override public String toString() { StringBuilder string = new StringBuilder(); string.append('size=').append(size).append(', ['); for (int i = 0; i < size; i++) { if (i != 0) { string.append(', '); } string.append(elements[i]); } string.append(']'); return string.toString(); }}
到此這篇關(guān)于Java使用數(shù)組實(shí)現(xiàn)ArrayList的動(dòng)態(tài)擴(kuò)容的方法的文章就介紹到這了,更多相關(guān)Java ArrayList動(dòng)態(tài)擴(kuò)容內(nèi)容請(qǐng)搜索好吧啦網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持好吧啦網(wǎng)!
相關(guān)文章:
1. IntelliJ IDEA設(shè)置默認(rèn)瀏覽器的方法2. IntelliJ IDEA設(shè)置背景圖片的方法步驟3. Spring security 自定義過濾器實(shí)現(xiàn)Json參數(shù)傳遞并兼容表單參數(shù)(實(shí)例代碼)4. docker /var/lib/docker/aufs/mnt 目錄清理方法5. JAMon(Java Application Monitor)備忘記6. Python TestSuite生成測(cè)試報(bào)告過程解析7. Python 的 __str__ 和 __repr__ 方法對(duì)比8. 學(xué)python最電腦配置有要求么9. Python Scrapy多頁(yè)數(shù)據(jù)爬取實(shí)現(xiàn)過程解析10. Python OpenCV去除字母后面的雜線操作
