List接口实现类-ArrayList、Vector、LinkedList集合深入学习以及源代码解析
學(xué)習(xí)List接口實(shí)現(xiàn)類 ArrayList? Vector? LinkedList
List接口的實(shí)現(xiàn)類中最經(jīng)常使用最重要的就是這三個(gè):ArrayList、Vector、LinkedList。
JDK中這三個(gè)類的定義:
1、ArrayList<E>:
??????? implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
??? private static final long serialVersionUID = 8683452581122892189L;
??? /**
???? * The array buffer into which the elements of the ArrayList are stored.
???? * The capacity of the ArrayList is the length of this array buffer.
???? */
??? private transient Object[] elementData;
??? /**
???? * The size of the ArrayList (the number of elements it contains).
???? *
???? * @serial
???? */
??? private int size;
2、 Vector<E>:
public class Vector<E>extends AbstractList<E>
??? implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
??? /**
???? * The array buffer into which the components of the vector are
???? * stored. The capacity of the vector is the length of this array buffer,
???? * and is at least large enough to contain all the vector's elements.
???? *
???? * <p>Any array elements following the last element in the Vector are null.
???? *
???? * @serial
???? */
??? protected Object[] elementData;
3、LinkedList<E>:
public class LinkedList<E>extends AbstractSequentialList<E>
??? implements List<E>, Deque<E>, Cloneable, java.io.Serializable
{?? // 在序列化這個(gè)對(duì)象的時(shí)候這個(gè)變量不會(huì)被這樣序列化
??? transient int size = 0;
??? /**
???? * Pointer to first node.
???? * Invariant: (first == null && last == null) ||
???? *??????????? (first.prev == null && first.item != null)
???? */
??? transient Node<E> first;
??? /**
???? * Pointer to last node.
???? * Invariant: (first == null && last == null) ||
???? *??????????? (last.next == null && last.item != null)
???? */
??? transient Node<E> last;
4、從這三個(gè)類定義就能夠看出一些信息:
(1)、三個(gè)都直接實(shí)現(xiàn)了AbstractList這個(gè)抽象類
(2)、ArrayList和Vector都實(shí)現(xiàn)了RandomAccess接口。而LinkedList沒(méi)有。這是什么意思呢??????????????
?在JDK 中,RandomAccess接口是一個(gè)空接口,所以它沒(méi)有實(shí)際意義。就是一個(gè)標(biāo)記,
?標(biāo)記這個(gè)類支持高速隨機(jī)訪問(wèn),所以,arrayList和 vector是支持隨機(jī)訪問(wèn)的,可是LinkedList不支持持?????????
(3)、serializbale接口表明他們都支持序列化。
5、以下具體說(shuō)說(shuō)List的這三個(gè)實(shí)現(xiàn):
? (1)、 查看實(shí)現(xiàn)源代碼會(huì)發(fā)現(xiàn)這三個(gè)里面,ArrayList和Vector使用了數(shù)組的實(shí)現(xiàn),相當(dāng)于封裝了對(duì)數(shù)組的操作。這也正是他們可以支持高速隨機(jī)訪問(wèn)的原因,多說(shuō)一句,JDK中全部基于數(shù)組實(shí)現(xiàn)的數(shù)據(jù)結(jié)構(gòu)都可以支持高速隨機(jī)訪問(wèn)。
?? ArrayList和Vector的實(shí)現(xiàn)上差點(diǎn)兒都使用了同樣的算法,他們的主要差別就是ArrayList沒(méi)有對(duì)不論什么一個(gè)方法做同步處理,所以不是線程安全的;而Vector中大部分方法都做了線程同步所以是線程安全的。
? (2)、LinkedList使用的是雙向循環(huán)鏈表的數(shù)據(jù)結(jié)構(gòu)。因?yàn)槭腔阪湵淼摹K詿o(wú)法法實(shí)現(xiàn)隨機(jī)訪問(wèn)的,僅僅能順序訪問(wèn),這也正是它沒(méi)有實(shí)現(xiàn)RandomAccess接口的原因。
? (3)、因?yàn)锳rrayList、Vector和LinkedList所採(cǎi)用的數(shù)據(jù)結(jié)構(gòu)不同。注定他們適用的是全然不同的場(chǎng)景。
通過(guò)閱讀這幾個(gè)類的源代碼,我們能夠看到他們實(shí)現(xiàn)的不同。
ArrayList和Vector基本一樣,我們就用ArrayList和LinkedList做對(duì)照。
在末尾添加一個(gè)元素
6、ArrayList中的add方法實(shí)現(xiàn)例如以下:
????? /**
???? * Appends the specified element to the end of this list.
???? *將元素加入到list的最后
???? * @param e element to be appended to this list
???? * @return <tt>true</tt> (as specified by {@link Collection#add})
???? */
??? public boolean add(E e) {
??????? // 推斷容量
??????? ensureCapacityInternal(size + 1);? // Increments modCount!!
??????? elementData[size++] = e;
??????? return true;
??? }
這種方法做兩件事情,首先確保數(shù)組空間足夠大。然后在數(shù)組末尾添加元素而且通過(guò)后++使得完畢size+1
從這個(gè)代碼能夠看出,假設(shè)數(shù)組空間足夠大,那么僅僅是數(shù)組的add操作就是O(1)的性能,很高效。
7、在看看ensureCapacityInternal這種方法的實(shí)現(xiàn):
?private void ensureCapacityInternal(int minCapacity) {
??????? modCount++;
??????? // overflow-conscious code。假設(shè)空間不夠則擴(kuò)容也就是又一次創(chuàng)建一個(gè)Object[]對(duì)象
??????? if (minCapacity - elementData.length > 0)
??????????? grow(minCapacity);
??? }
8、grow(int minCapacity)方法創(chuàng)建數(shù)組并將原來(lái)的數(shù)據(jù)復(fù)制到新數(shù)組中
? /**
???? * Increases the capacity to ensure that it can hold at least the
???? * number of elements specified by the minimum capacity argument.
???? *
???? * @param minCapacity the desired minimum capacity
???? */
??? private void grow(int minCapacity) {
??????? // overflow-conscious code
??????? int oldCapacity = elementData.length;
??????? // 使用移位運(yùn)算,提高效率
?????? int newCapacity = oldCapacity + (oldCapacity >> 1);
??????? if (newCapacity - minCapacity < 0)
??????????? newCapacity = minCapacity;
??????? if (newCapacity - MAX_ARRAY_SIZE > 0)
??????????? newCapacity = hugeCapacity(minCapacity);
??????? // minCapacity is usually close to size, so this is a win:
??????? elementData = Arrays.copyOf(elementData, newCapacity);
??? }
能夠看出。假設(shè)數(shù)組空間不夠,那么這種方法就會(huì)做數(shù)組擴(kuò)容和數(shù)組復(fù)制操作,看上面,JDK利用移位運(yùn)算符進(jìn)行擴(kuò)容計(jì)算,>>1右移一位表示除2,所以newCapacity就是擴(kuò)容為原來(lái)的1.5倍。
9、這里的代碼都是JDK1.7中的實(shí)現(xiàn),JDK1.7對(duì)1.6的非常多代碼做了優(yōu)化。比方上面這段擴(kuò)容代碼,在JDK1.6中上面的是直接除2,顯然。移位運(yùn)算要更高效。
10、在看看LinkedList中的add方法:
(1)、add(E e):
/**
???? * Appends the specified element to the end of this list.
???? *
???? * <p>This method is equivalent to {@link #addLast}.
???? *
???? * @param e element to be appended to this list
???? * @return {@code true} (as specified by {@link Collection#add})
???? */
??? public boolean add(E e) {
??????? linkLast(e);
??????? return true;
??? }
(2)、linkLast(E e) :
??? /**
???? * Links e as last element.
???? */
??? void linkLast(E e) {
??????? final Node<E> l = last;
??????? final Node<E> newNode = new Node<>(l, e, null);
??????? last = newNode;
??????? if (l == null)
??????????? first = newNode;
??????? else
??????????? l.next = newNode;
??????? size++;
??????? modCount++;
??? }
(3)、內(nèi)部類:
? private static class Node<E> {
?????? // 當(dāng)前節(jié)點(diǎn)
? ? ? ? E item;
?????? // 當(dāng)前節(jié)點(diǎn)的后節(jié)點(diǎn)
? ? ? ? Node<E> next;
?????? // 當(dāng)前節(jié)點(diǎn)的前節(jié)點(diǎn)
? ? ??? Node<E> prev;
????? // 構(gòu)造函數(shù)
??????? Node(Node<E> prev, E element, Node<E> next) {
??????????? this.item = element;
??????????? this.next = next;
??????????? this.prev = prev;
??????? }
??? }
從這段add代碼能夠看出。LinkedList因?yàn)槭褂昧随湵怼K圆豁氁M(jìn)行擴(kuò)容,直接把元素加到鏈表最后,把新元素的前驅(qū)指向之前的last元素。并把last元素指向新元素就能夠了。
這也是一個(gè)O(1)的性能。
在任何位置插入元素
11、ArrayList中的實(shí)現(xiàn)例如以下:
(1)、 add(int index, E element)
public void add(int index, E element) {
??????? rangeCheckForAdd(index);
???????? // 推斷容量
??????? ensureCapacityInternal(size + 1);? // Increments modCount!!
??????? System.arraycopy(elementData, index, elementData, index + 1,
???????????????????????? size - index);
??????? elementData[index] = element;
??????? size++;
??? }
private void ensureCapacityInternal(int minCapacity) {
??????? modCount++;
??????? // overflow-conscious code,假設(shè)數(shù)組長(zhǎng)度不夠則進(jìn)行擴(kuò)容
??????? if (minCapacity - elementData.length > 0)
??????????? grow(minCapacity);
??? }
(3)、grow(int minCapacity)
/**
???? * Increases the capacity to ensure that it can hold at least the
???? * number of elements specified by the minimum capacity argument.
???? *
???? * @param minCapacity the desired minimum capacity
???? */
??? 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)
??????????? newCapacity = hugeCapacity(minCapacity);
??????? // minCapacity is usually close to size, so this is a win:
??????? elementData = Arrays.copyOf(elementData, newCapacity);
??? }
這段代碼,首先先檢查數(shù)組容量,容量不夠先擴(kuò)容。然后把index之后的數(shù)組往后挪一個(gè)。最后在index位置放上新元素。因?yàn)閿?shù)組是一塊連續(xù)內(nèi)存空間,所以在任何位置插入。都會(huì)導(dǎo)致這個(gè)其后數(shù)組后挪一位的情況。須要做一次數(shù)組復(fù)制操作,非常明顯,假設(shè)有大量的隨機(jī)插入,那么這個(gè)數(shù)組復(fù)制操作開(kāi)銷會(huì)非常大。并且插入的越靠前,數(shù)組復(fù)制開(kāi)銷越大。
12、LinkedList中的實(shí)現(xiàn):
(1)、add(int index, E element)
???? * Inserts the specified element at the specified position in this list.
???? * Shifts the element currently at that position (if any) and any
???? * subsequent elements to the right (adds one to their indices).
???? *
???? * @param index index at which the specified element is to be inserted
???? * @param element element to be inserted
???? * @throws IndexOutOfBoundsException {@inheritDoc}
???? */
??? public void add(int index, E element) {
??????? checkPositionIndex(index);
??????? if (index == size)
??????????? linkLast(element);
??????? else
??????????? linkBefore(element, node(index));
??? }
(2)、linkBefore(E e, Node<E> succ)
?/**
???? * Inserts element e before non-null Node succ.
???? */
??? void linkBefore(E e, Node<E> succ) {
??????? // assert succ != null;
??????? final Node<E> pred = succ.prev;
??????? final Node<E> newNode = new Node<>(pred, e, succ);
??????? succ.prev = newNode;
??????? if (pred == null)
??????????? first = newNode;
??????? else
??????????? pred.next = newNode;
??????? size++;
??????? modCount++;
??? }
(3)、 Node<E> node(int index)
/**
???? * Returns the (non-null) Node at the specified element index.
???? */
??? Node<E> node(int index) {
??????? // assert isElementIndex(index);
??????? if (index < (size >> 1)) {
??????????? Node<E> x = first;
??????????? for (int i = 0; i < index; i++)
??????????????? x = x.next;
??????????? return x;
??????? } else {
??????????? Node<E> x = last;
??????????? for (int i = size - 1; i > index; i--)
??????????????? x = x.prev;
??????????? return x;
??????? }
??? }
這段代碼。取到原先index處節(jié)點(diǎn)的前驅(qū)。變成新節(jié)點(diǎn)的前驅(qū),同一時(shí)候把原先index變成新節(jié)點(diǎn)的后驅(qū),這樣就完畢了新節(jié)點(diǎn)的插入。
這個(gè)就是鏈表的優(yōu)勢(shì)。不存在數(shù)據(jù)復(fù)制操作,性能和在最后插入是一樣的。
小結(jié):
??? 從上面的源代碼剖析能夠看出這三種List實(shí)現(xiàn)的一些典型適用場(chǎng)景,假設(shè)常常對(duì)數(shù)組做隨機(jī)插入操作,特別是插入的比較靠前,那么LinkedList的性能優(yōu)勢(shì)就很明顯。而假設(shè)都僅僅是末尾插入,則ArrayList更占領(lǐng)優(yōu)勢(shì)。假設(shè)須要線程安全。則使用Vector或者創(chuàng)建線程安全的ArrayList。
在使用基于數(shù)組實(shí)現(xiàn)的ArrayList 和Vector 時(shí)我們要指定初始容量。由于我們?cè)谠创a中也看到了,在加入時(shí)首先要進(jìn)行容量的推斷,假設(shè)容量不夠則要?jiǎng)?chuàng)建新數(shù)組。還要將原來(lái)數(shù)組中的數(shù)據(jù)拷貝到新數(shù)組中。這個(gè)過(guò)程會(huì)減低效率而且會(huì)浪費(fèi)資源。
?
轉(zhuǎn)載于:https://www.cnblogs.com/mfrbuaa/p/5157768.html
總結(jié)
以上是生活随笔為你收集整理的List接口实现类-ArrayList、Vector、LinkedList集合深入学习以及源代码解析的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: JQuery 的跨域方法 可跨任意网站
- 下一篇: 第八届河南省省赛