「 深入浅出 」集合List
第一篇文章?「 深入淺出 」java集合Collection和Map?主要講了對(duì)集合的整體介紹,本篇文章主要講List相對(duì)于Collection新增的一些重要功能以及其重要子類ArrayList、LinkedList、Vector
一、List集合
關(guān)于List集合的介紹與方法,可參考第一篇文章?「 深入淺出 」java集合Collection和Map
迭代方法ListIterator
相對(duì)于其它集合,List集合添加了一種新的迭代方法ListIterator
ListIterator的方法如下:
ListIterator接口在Iterator接口基礎(chǔ)上增加了如下方法:
boolean hasPrevious(): 如果以逆向遍歷列表。如果迭代器有上一個(gè)元素,則返回 true。
E previous():返回迭代器的前一個(gè)元素。
void add(Object o):將指定的元素插入列表。
int nextIndex():下一個(gè)索引號(hào)
int previousIndex():上一個(gè)索引號(hào)
void set(E e):修改迭代器當(dāng)前元素的值
void add(E e):在迭代器當(dāng)前位置插入一個(gè)元素
ListIterator接口比Iterator接口多了兩個(gè)功能:
1.ListIterator可在遍歷過(guò)程中新增和修改
2.ListIterator可逆向遍歷
使用示例如下:
public class ListIteratorDemo {public static void main(String[] args) {// 創(chuàng)建列表List<Integer> list = new ArrayList<Integer>();// 向列表中增加10個(gè)元素for (int i = 0; i < 10; i++) {list.add(i);}// 獲得ListIterator對(duì)象ListIterator<Integer> it = list.listIterator();// 正序遍歷修改與新增while (it.hasNext()) {Integer i = it.next();//修改元素值it.set(i+1);if(i == 5 ){//新增元素值it.add(55);}//! it.set(i+1);// 注意:如果修改的代碼在這個(gè)位置會(huì)報(bào)錯(cuò)//set操作不能放在add操作之后// 這里不做解析,欲知詳情,請(qǐng)看源碼}System.out.println("正向遍歷");//正向遍歷for(Integer i:list){System.out.println(i+" ");}System.out.println("逆向遍歷");//逆向遍歷//經(jīng)過(guò)上面迭代器it遍歷后,迭代器it已到達(dá)最后一個(gè)節(jié)點(diǎn)while (it.hasPrevious()) {System.out.println(it.previous() + " ");}} }二、ArrayList和Vector
ArrayList和Vector很相似,所以就一起介紹了
ArrayList和Vector類都是基于數(shù)組實(shí)現(xiàn)的List類,所以ArrayList和Vector類封裝了一個(gè)動(dòng)態(tài)的、允許再分配的Object[]數(shù)組。ArrayList和Vector對(duì)象使用initalCapacity參數(shù)來(lái)設(shè)置該數(shù)組的長(zhǎng)度,當(dāng)向ArrayList和Vector中添加元素超過(guò)了該數(shù)組的長(zhǎng)度時(shí),它們的initalCapacity會(huì)自動(dòng)增加。
下面我們通過(guò)閱讀JDK 1.8 ArrayList源碼來(lái)了解ArrayList
無(wú)參構(gòu)造函數(shù)
默認(rèn)初始化為容量為10
???/*** Constructs an empty list with an initial capacity of ten。意思是:構(gòu)造一個(gè)空數(shù)組,默認(rèn)的容量為10*/public ArrayList() {this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;}有參構(gòu)造函數(shù)
創(chuàng)建指定容量的ArrayList
//動(dòng)態(tài)Object數(shù)組,用來(lái)保存加入到ArrayList的元素 Object[] elementData;//ArrayList的構(gòu)造函數(shù),傳入?yún)?shù)為數(shù)組大小 public ArrayList(int initialCapacity) {if (initialCapacity > 0) {//創(chuàng)建一個(gè)對(duì)應(yīng)大小的數(shù)組對(duì)象this.elementData = new Object[initialCapacity];} else if (initialCapacity == 0) {//傳入數(shù)字為0,將elementData 指定為一個(gè)靜態(tài)類型的空數(shù)組this.elementData = EMPTY_ELEMENTDATA;} else {throw new IllegalArgumentException("Illegal Capacity: "+initialCapacity);}}add方法
執(zhí)行add方法時(shí),先確保容量足夠大,若容量不夠,則會(huì)進(jìn)行擴(kuò)容;
擴(kuò)容大小為原來(lái)的1.5倍(這個(gè)需要注意一下,面試經(jīng)常考)
remove方法
有以下兩種刪除方法:
- remove(int index)是針對(duì)于索引來(lái)進(jìn)行刪除,不需要去遍歷整個(gè)集合,效率更高;
- remove(Object o)是針對(duì)于對(duì)象來(lái)進(jìn)行刪除,需要遍歷整個(gè)集合進(jìn)行equals()方法比對(duì),所以效率較低;
不過(guò),無(wú)論是哪種形式的刪除,最終都會(huì)調(diào)用System.arraycopy()方法進(jìn)行數(shù)組復(fù)制操作,等同于移動(dòng)數(shù)組位置,所以效率都會(huì)受到影響
//在ArrayList的移除index位置的元素 public E remove(int index) {//檢查索引是否合法:不合法拋異常rangeCheck(index);//操作數(shù)+1:modCount++;//獲取當(dāng)前索引的value:E oldValue = elementData(index);//獲取需要?jiǎng)h除元素 到最后一個(gè)元素的長(zhǎng)度,也就是刪除元素后,后續(xù)元素移動(dòng)的個(gè)數(shù);int numMoved = size - index - 1;//如果移動(dòng)元素個(gè)數(shù)大于0 ,也就是說(shuō)刪除的不是最后一個(gè)元素:if (numMoved > 0)// 將elementData數(shù)組index+1位置開(kāi)始拷貝到elementData從index開(kāi)始的空間System.arraycopy(elementData, index+1, elementData, index, numMoved);//size減1,并將最后一個(gè)元素置為nullelementData[--size] = null;//返回被刪除的元素:return oldValue; }//在ArrayList的移除對(duì)象為O的元素,不返回被刪除的元素: public boolean remove(Object o) {//如果o==null,則遍歷集合,判斷哪個(gè)元素為null:if (o == null) {for (int index = 0; index < size; index++)if (elementData[index] == null) {//快速刪除,和前面的remove(index)一樣的邏輯fastRemove(index);return true;}} else {//同理:for (int index = 0; index < size; index++)if (o.equals(elementData[index])) {fastRemove(index);return true;}}return false; }//快速刪除: private void fastRemove(int index) {//操作數(shù)+1modCount++;//獲取需要?jiǎng)h除元素 到最后一個(gè)元素的長(zhǎng)度,也就是刪除元素后,后續(xù)元素移動(dòng)的個(gè)數(shù);int numMoved = size - index - 1;//如果移動(dòng)元素個(gè)數(shù)大于0 ,也就是說(shuō)刪除的不是最后一個(gè)元素:if (numMoved > 0)// 將elementData數(shù)組index+1位置開(kāi)始拷貝到elementData從index開(kāi)始的空間System.arraycopy(elementData, index+1, elementData, index, numMoved);//size減1,并將最后一個(gè)元素置為nullelementData[--size] = null; }get方法
通過(guò)elementData()方法獲取對(duì)應(yīng)索引元素,在返回時(shí)候進(jìn)行類型轉(zhuǎn)換
//獲取index位置的元素 public E get(int index) {//檢查index是否合法:rangeCheck(index);//獲取元素:return elementData(index); } //獲取數(shù)組index位置的元素:返回時(shí)類型轉(zhuǎn)換 E elementData(int index) {return (E) elementData[index]; }set方法
通過(guò)elementData獲取舊元素,再設(shè)置新元素值相應(yīng)index位置,最后返回舊元素
//設(shè)置index位置的元素值了element,返回該位置的之前的值 public E set(int index, E element) {//檢查index是否合法:判斷index是否大于sizerangeCheck(index);//獲取該index原來(lái)的元素:E oldValue = elementData(index);//替換成新的元素:elementData[index] = element;//返回舊的元素:return oldValue; }調(diào)整容量大小
ArrayList還提供了兩個(gè)額外的方法來(lái)調(diào)整其容量大小
- void ensureCapacity(int minCapacity): 增加容量,以確保它至少能夠容納最小容量參數(shù)所指定的元素?cái)?shù)。
- void trimToSize():將容量調(diào)整為列表的當(dāng)前大小。
Vector實(shí)現(xiàn)原理與ArrayList基本相同,可參考上述內(nèi)容
ArrayList和Vector的主要區(qū)別
- ArrayList是線程不安全的,Vector是線程安全的。
- Vector的性能比ArrayList差。
LinkedList
LinkedList是基于雙向鏈表實(shí)現(xiàn)的,內(nèi)部存儲(chǔ)主要是Node對(duì)象,該對(duì)象存儲(chǔ)著元素值外,還指向上一節(jié)點(diǎn)和下一節(jié)點(diǎn)。
注意,因?yàn)長(zhǎng)inkedList是基于鏈表實(shí)現(xiàn)的,沒(méi)有容量的說(shuō)法,所以更沒(méi)有擴(kuò)容之說(shuō)
集合基礎(chǔ)框架
public class LinkedList<E>extends AbstractSequentialList<E>implements List<E>, Deque<E>, Cloneable, java.io.Serializable {//LinkedList的元素個(gè)數(shù):transient int size = 0;//LinkedList的頭結(jié)點(diǎn):Node內(nèi)部類transient java.util.LinkedList.Node<E> first;//LinkedList尾結(jié)點(diǎn):Node內(nèi)部類transient java.util.LinkedList.Node<E> last;//空實(shí)現(xiàn):頭尾結(jié)點(diǎn)均為null,鏈表不存在public LinkedList() {}//調(diào)用添加方法:public LinkedList(Collection<? extends E> c) {this();addAll(c);}//節(jié)點(diǎn)的數(shù)據(jù)結(jié)構(gòu),包含前后節(jié)點(diǎn)的引用和當(dāng)前節(jié)點(diǎn)private static class Node<E> {//結(jié)點(diǎn)元素:E item;//結(jié)點(diǎn)后指針java.util.LinkedList.Node<E> next;//結(jié)點(diǎn)前指針java.util.LinkedList.Node<E> prev;Node(java.util.LinkedList.Node<E> prev, E element, java.util.LinkedList.Node<E> next) {this.item = element;this.next = next;this.prev = prev;}} }LinkedList的常用方法只做簡(jiǎn)單介紹不貼源碼,具體可自行看源碼
add方法
LinkedList有兩種添加方法
- add(E e)鏈表最后添加一個(gè)元素
- add(int index, E element)指定位置下添加一個(gè)元素;
LinedList添加元素主要分為以下步驟:
1.將添加的元素轉(zhuǎn)換為L(zhǎng)inkedList的Node對(duì)象節(jié)點(diǎn);
2.增加該Node節(jié)點(diǎn)的前后引用,即該Node節(jié)點(diǎn)的prev、next屬性,讓其分別指上、下節(jié)點(diǎn);
3.修改該Node節(jié)點(diǎn)的前后Node節(jié)點(diǎn)中pre/next屬性,使其指向該節(jié)點(diǎn)。
remove方法
LinkedList的刪除也提供了2種形式
- remove(int index)直接通過(guò)索引刪除元素
- remove(Object o)通過(guò)對(duì)象刪除元素,需要逐個(gè)遍歷LinkedList的元素,重復(fù)元素只刪除第一個(gè):
刪除后,需要修改上節(jié)點(diǎn)的next指向當(dāng)前下一節(jié)點(diǎn),下節(jié)點(diǎn)的prev指向當(dāng)前上一節(jié)點(diǎn)
set方法
set(int index, E element)方法通過(guò)node(index)獲取到相應(yīng)的Node,再修改元素的值
get方法
這是我們最常用的方法,其中核心方法node(int index),需要從頭遍歷或從后遍歷找到相應(yīng)Node節(jié)點(diǎn)
在通過(guò)node(int index)獲取到對(duì)應(yīng)節(jié)點(diǎn)后,返回節(jié)點(diǎn)中的item屬性,該屬性就是我們所保存的元素。
ArrayList和LinkedList的主要區(qū)別
- ArrayList基于數(shù)組實(shí)現(xiàn)的,LinkedList是基于雙向鏈表實(shí)現(xiàn)的
- ArrayList隨機(jī)訪問(wèn)效率高,隨機(jī)插入、隨機(jī)刪除效率低,需要移動(dòng)元素位置
LinkedList隨機(jī)插入、隨機(jī)刪除效率高,隨機(jī)訪問(wèn)效率低,因需要遍歷鏈表
好叻,搞完,溜了溜了
下一期為<集合Set>,敬請(qǐng)期待
近期推薦:
好人?壞人?做真實(shí)的人
「 優(yōu)質(zhì)資源 」收藏!最新精選優(yōu)質(zhì)資源!
java小心機(jī)(5)| 淺談?lì)惓蓡T初始化順序
更多精彩內(nèi)容,可閱讀原文
您的點(diǎn)贊、轉(zhuǎn)發(fā)是對(duì)我最大的支持!
?THANDKS
- End -
一個(gè)立志成大腿而每天努力奮斗的年輕人
伴學(xué)習(xí)伴成長(zhǎng),成長(zhǎng)之路你并不孤單!
總結(jié)
以上是生活随笔為你收集整理的「 深入浅出 」集合List的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: python数据结构与算法(13)
- 下一篇: 线程(Thread,ThreadPool