Java?數(shù)據(jù)結(jié)構(gòu)深入理解ArrayList與順序表
一、ArrayList簡介
在集合框架中,ArrayList是一個(gè)普通的類,實(shí)現(xiàn)了List接口,具體框架圖如下:
ArrayList底層是一段連續(xù)的空間,并且可以動(dòng)態(tài)擴(kuò)容,是一個(gè)動(dòng)態(tài)類型的順序表。
二、ArrayList的使用
1、ArrayList的構(gòu)造
方法 | 使用 |
---|---|
ArrayList() | 無參構(gòu)造 |
ArrayList(Collection<? extends E> c) | 利用其他 Collection 構(gòu)建 ArrayList |
ArrayList(int initialCapacity) | 指定順序表初始容量 |
public static void main(String[] args) { // 無參構(gòu)造 List<Integer> list1 = new ArrayList<>(); // 給定初始容量 List<Integer> list2 = new ArrayList<>(10); // 使用另外一個(gè) ArrayList對(duì)其初始化 List<Integer> list3 = new ArrayList<>(list2); list1.add(1); list1.add(2); list1.add(3); // 其父類 AbstractCollection重寫了 toString方法 System.out.println(list1);// 輸出 [1, 2, 3] }
2、ArrayList的遍歷
1、遍歷順序表
2、for - each(實(shí)現(xiàn)了Iterable接口)
3、迭代器(實(shí)現(xiàn)了Iterable接口)
// 遍歷順序表 for (int i = 0; i < list.size(); i++) { System.out.print(list.get(i)); } // for - each 遍歷 for (String s : list) { System.out.print(s); } // 迭代器打印 // 獲取迭代器對(duì)象 Iterator<String> iterator = list.iterator(); while (iterator.hasNext()) { // 獲取下一個(gè)對(duì)象 String next = iterator.next(); // 打印 System.out.print(next); } // listIterator ---- 實(shí)現(xiàn)了 Iterator 接口 ListIterator<String> iterator2 = list.listIterator(); while (iterator2.hasNext()) { String next = iterator2.next(); System.out.print(next); }
這里的 listIterator 實(shí)現(xiàn)了 Iterator 接口,從方法上,listIterator 有更多的功能(方法),例如在遍歷的時(shí)候,進(jìn)行添加元素 add()。
ListIterator<String> iterator2 = list.listIterator(); while (iterator2.hasNext()) { String next = iterator2.next(); if (next.equals("hello")) { iterator2.add("三團(tuán)");// 在 hello 的后面添加 三團(tuán) }else{ System.out.print(next + " "); } } System.out.println(list);// [hello, 三團(tuán), bit, world]
3、ArrayList的常見操作(方法)
方法 | 解釋 |
---|---|
boolean add(E e) | 尾插 e |
void add(int index, E element) | 將 e 插入到 index 位置 |
boolean addAll(Collection<? extends E> c) | 將集合 c 中的元素 尾插到該集合中 |
E remove(int index) | 刪除 index 位置元素并返回 |
boolean remove(Object o) | 刪除遇到的第一個(gè) o |
E get(int index) | 獲取下標(biāo) index 位置元素 |
E set(int index, E element) | 將下標(biāo) index 位置元素設(shè)置為 element |
void clear() | 清空順序表 |
boolean contains(Object o) | 判斷 o 是否在線性表中 |
int indexOf(Object o) | 返回第一個(gè) o 所在下標(biāo) |
int lastIndexOf(Object o) | 返回最后一個(gè) o 的下標(biāo) |
List< E > subList(int fromIndex, int toIndex) | 截取部分 list |
List<String> list = new ArrayList<>(); List<String> listAdd = new ArrayList<>(); listAdd.add("hello"); listAdd.add("world"); listAdd.add("你好~"); list.add("哈哈");// 尾插元素 list.add(0,"你好"); // 0 下標(biāo)插入 "你好 " list.addAll(listAdd);// 將集合 listAdd 中的元素尾插到該集合中 String s = list.remove(0);// 刪除 index 位置元素并返回 boolean s2 = list.remove("hello");// 刪除遇到的第一個(gè) hello,沒找到則返回 false list.set(0,"we"); list.indexOf("we");//返回第一個(gè) "we" 所在下標(biāo) list.lastIndexOf("we");// 返回最后一個(gè) "we" 的下標(biāo) System.out.println(list); // 截取子串 -- 左閉右開區(qū)間 List<String> sub = list.subList(1, 3); System.out.println(sub); list.set(2,"修改后的list"); System.out.println(sub);
注意: 這里的 subList方法,并不是真正的返回一個(gè)截取部分的新地址,而是將原地址的截取部分返回,所以當(dāng)修改原來的線性表中的元素時(shí),子串中的內(nèi)容也會(huì)發(fā)生改變。
4、ArrayList的擴(kuò)容機(jī)制
1、當(dāng)調(diào)用無參構(gòu)造時(shí),即List< String > list = new ArrayList<>(),底層還沒有分配真正的內(nèi)存(初始化是一個(gè)空數(shù)組),初始容量為 0。當(dāng)?shù)谝淮翁砑釉兀ㄕ{(diào)用 add 方法) 時(shí),整個(gè)順序表的容量被擴(kuò)充為10,放滿后,以 1.5 倍擴(kuò)容。
2、當(dāng)調(diào)用帶容量的構(gòu)造方法時(shí),例如 List< String > list = new ArrayList<>(16),順序表初始容量就為16,放滿后以 1.5 倍擴(kuò)容。
結(jié)論
如果調(diào)用無參構(gòu)造方法,順序表初始大小為0,當(dāng)?shù)谝淮畏湃朐貢r(shí),整個(gè)順序表容量變?yōu)?0,當(dāng)放滿10個(gè)元素,進(jìn)行1.5倍擴(kuò)容。
如果調(diào)用給定容量的構(gòu)造方法,初始大小就是給定的容量,當(dāng)放滿了,就進(jìn)行1.5倍擴(kuò)容。
三、模擬實(shí)現(xiàn)一個(gè)順序表(Object[])
public class MyArrayList<E> { private Object[] elementData;// 數(shù)組 private int usedSize;// 代表有效的數(shù)據(jù)個(gè)數(shù) private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; public MyArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; } public MyArrayList(int capacity) { // 判斷參數(shù)的合法性 if (capacity >= 0) { this.elementData = new Object[capacity]; }else { throw new RuntimeException("初始化的容量不能為負(fù)數(shù)!"); } } /** * 添加元素 * @param e 要添加的元素 */ public boolean add(E e) { // 確定一個(gè)真正的容量,預(yù)測(cè) -> 如需擴(kuò)容則擴(kuò)容 ensureCapacityInternal(usedSize + 1); // 擴(kuò)容完畢,放數(shù)據(jù) elementData[usedSize++] = e; return true; } /** * 給 index位置添加元素 * @param index * @param e */ public boolean add(int index, E e) { // 檢查 index 是否合法 rangeCheckForAdd(index); // 確定一個(gè)真正的容量 -> 如需擴(kuò)容則擴(kuò)容 ensureExplicitCapacity(usedSize + 1); // 移動(dòng) index后面的元素,并在 index位置插入元素 copy(index,e); usedSize++; return true; } private void copy(int index, E e){ for (int i = usedSize; i > index; i--) { elementData[i] = elementData[i - 1]; } elementData[index] = e; } private void rangeCheckForAdd(int index) { if (index > usedSize || index < 0) throw new IndexOutOfBoundsException("index位置不合法!"); } public void ensureCapacityInternal(int minCapacity) { // 計(jì)算出需要的容量 int capacity = calculateCapacity(elementData, minCapacity); // 根據(jù)計(jì)算出的容量,看是否需要擴(kuò)容或者分配內(nèi)存 ensureExplicitCapacity(capacity); } private void ensureExplicitCapacity(int minCapacity) { // 如果需要的容量大于數(shù)組容量,就擴(kuò)容 if (minCapacity - elementData.length > 0) // 擴(kuò)容 grow(minCapacity); } private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; private void grow(int minCapacity) { // overflow-conscious code int oldCapacity = elementData.length; int newCapacity = oldCapacity + (oldCapacity >> 1); if (newCapacity - minCapacity < 0) newCapacity = minCapacity; if (newCapacity - MAX_ARRAY_SIZE > 0) // 說明你要的容量非常大,就分配更大的內(nèi)存 newCapacity = hugeCapacity(minCapacity);; elementData = Arrays.copyOf(elementData, newCapacity); } private static int hugeCapacity(int minCapacity) { if (minCapacity < 0) // overflow throw new OutOfMemoryError(); return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE; } private static int calculateCapacity(Object[] elementData, int minCapacity) { // 確定之前數(shù)組是否分配過內(nèi)存,沒有的話返回一個(gè)初始化的容量 10 if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { return Math.max(10, minCapacity); } // 分配后,返回 +1 后的值,即實(shí)際所需要的容量 return minCapacity; } }
到此這篇關(guān)于Java 數(shù)據(jù)結(jié)構(gòu)深入理解ArrayList與順序表的文章就介紹到這了,更多相關(guān)Java ArrayList內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
詳解SpringMVC組件之HandlerMapping(一)
這篇文章主要介紹了詳解SpringMVC組件之HandlerMapping(一),HandlerMapping組件是Spring?MVC核心組件,用來根據(jù)請(qǐng)求的request查找對(duì)應(yīng)的Handler,在Spring?MVC中,有各式各樣的Web請(qǐng)求,每個(gè)請(qǐng)求都需要一個(gè)對(duì)應(yīng)的Handler來處理,需要的朋友可以參考下2023-08-08springboot本地調(diào)試沒問題,打包運(yùn)行報(bào)錯(cuò)原因及分析
這篇文章主要介紹了springboot本地調(diào)試沒問題,打包運(yùn)行報(bào)錯(cuò)原因及分析,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-05-05SpringBoot常用數(shù)據(jù)庫開發(fā)技術(shù)匯總介紹
Spring Boot常用的數(shù)據(jù)庫開發(fā)技術(shù)有JDBCTemplate、JPA和Mybatis,它們分別具有不同的特點(diǎn)和適用場(chǎng)景,可以根據(jù)具體的需求選擇合適的技術(shù)來進(jìn)行開發(fā)2023-04-04Java編程線程同步工具Exchanger的使用實(shí)例解析
這篇文章主要介紹了Java編程線程同步工具Exchanger的使用實(shí)例解析,分享了相關(guān)代碼示例,小編覺得還是挺不錯(cuò)的,具有一定借鑒價(jià)值,需要的朋友可以參考下2018-02-02JVM默認(rèn)時(shí)區(qū)為:Asia/Shanghai與java程序中GMT+08不一致異常
這篇文章主要介紹了JVM默認(rèn)時(shí)區(qū)為:Asia/Shanghai與java程序中GMT+08不一致異常問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-10-10Java中replace與replaceAll的區(qū)別與測(cè)試
replace和replaceAll是JAVA中常用的替換字符的方法,下面這篇文章主要給大家介紹了關(guān)于Java中replace與replaceAll的區(qū)別與測(cè)試,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-09-09java實(shí)現(xiàn)動(dòng)態(tài)時(shí)鐘并設(shè)置鬧鐘功能
這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)動(dòng)態(tài)時(shí)鐘并設(shè)置鬧鐘功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-01-01spring的pointcut正則表達(dá)式的實(shí)現(xiàn)
本文主要介紹了spring的pointcut正則表達(dá)式的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-08-08