JAVA提高第十篇 ArrayList深入分析
前面一章節(jié),我們介紹了集合的類(lèi)圖,那么本節(jié)將學(xué)習(xí)Collection 接口中最常用的子類(lèi)ArrayList類(lèi),本章分為下面幾部分講解(說(shuō)明本章采用的JDK1.6源碼進(jìn)行分析,因?yàn)閭€(gè)人認(rèn)為雖然JDK1.8進(jìn)行了部分改動(dòng),但萬(wàn)變不離其宗,仍然采用的JDK1.6的引子進(jìn)行的優(yōu)化,因此學(xué)會(huì)了1.6對(duì)于1.8也就理解了)。
一、ArrayList 的常見(jiàn)功能
在分析ArrayList的源碼前,我們先看下ArrayList的常見(jiàn)的功能:
package study.collection;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
public class TestDemo01
{
public static void main(String[] args)
{
List list = new ArrayList();
//ArrayList:底層實(shí)現(xiàn)時(shí)數(shù)組,線程不安全,效率高。所以,查詢(xún)快。修改、插入、刪除慢。
//LinkedList:底層實(shí)現(xiàn)是鏈表,線程不安全,效率高。所以,查詢(xún)慢。修改、插入、刪除快。
//Vector:線程安全的,效率低。
list.add("aaa");
list.add("aaa");
list.add(new Date());
list.add(new Dog());
list.add(1234); //注意,list集合中只能添加引用類(lèi)型,這里包裝類(lèi)的:自動(dòng)裝箱!
list.remove(new String("aaa"));
System.out.println(list.size());
for(int i=0;i<list.size();i++){
System.out.println(list.get(i));
}
list.set(3, new String("3333"));
list.add(4, new String("3333"));
System.out.println(list.isEmpty());
list.remove(new Dog()); //hashcode和equals
System.out.println(list.size());
List list2 = new ArrayList();
list2.add("bbb");
list2.add("ccc");
list.add(list2);
//跟順序的操作
String str = (String) list.get(0);
System.out.println(str);
list.set(1, "ababa");
list.remove(0);
}
}
class Dog
{
}
從上述可以看到了,ArrayList 接口中除了繼承自父類(lèi)Collection 接口中的方法外,還實(shí)現(xiàn)了List接口中擴(kuò)充的和索引相關(guān)的方法,這都源于其底層為數(shù)組結(jié)構(gòu)。
二、ArrayList 的重要的屬性
上面的部分列舉了ArrayList中常見(jiàn)的一些功能方法,那么這些方法又是如何使用的呢,下面我們將進(jìn)行源碼的剖析,在剖析前,我們可以自己思考下,我們知道ArrayList 是一個(gè)動(dòng)態(tài)擴(kuò)展的集合,之所以動(dòng)態(tài)擴(kuò)展的原因或者說(shuō)比數(shù)組強(qiáng)的地方肯定就在于數(shù)組的長(zhǎng)度是固定的,不能擴(kuò)展,這是數(shù)組的最大缺陷,所以才有了集合,那么ArrayList,那么其底層肯定也采用的是數(shù)組結(jié)構(gòu),因?yàn)樗蠥rrayList嘛,那么其重要的屬性之一,必然是定義了一個(gè)數(shù)組。如下:
public class ArrayList<E> extends AbstractList<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;
ArrayList就是一個(gè)以數(shù)組形式實(shí)現(xiàn)的集合,其元素的功能為:
private transient Object[] elementData; //ArrayList是基于數(shù)組的一個(gè)實(shí)現(xiàn),elementData就是底層的數(shù)組
private int size; //ArrayList里面元素的個(gè)數(shù),這里要注意一下,size是按照調(diào)用add、remove方法的次數(shù)進(jìn)行自增或者自減的,所以add了一個(gè)null進(jìn)入ArrayList,size也會(huì)加1
三、ArrayList 的構(gòu)造方法分析
在分析完上面的屬性后,我們緊接著來(lái)看下ArrayList的構(gòu)造方法:
/**
*構(gòu)造一個(gè)具有指定容量的list
*/
public ArrayList(int initialCapacity) {
super();
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
this.elementData = new Object[initialCapacity];
}
/**
* 構(gòu)造一個(gè)初始容量為10的list,也就說(shuō)當(dāng)我們經(jīng)常采用new ArrayList()的時(shí)候,實(shí)際分配的大小就為10
*/
public ArrayList() {
this(10);
}
/**
*構(gòu)造一個(gè)包含指定元素的list,這些元素的是按照Collection的迭代器返回的順序排列的
*/
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
size = elementData.length;
// c.toArray might (incorrectly) not return Object[] (see 6260652) 這里是因?yàn)閠oArray可能不一定是Object類(lèi)型的,因此判斷如果不是就進(jìn)行了拷貝轉(zhuǎn)換操作
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
}
可以看到,ArrayList 提供了三個(gè)構(gòu)造方法,分別的含義,已經(jīng)注釋到代碼上面了,那么想一下指定容量的構(gòu)造方法的意義,既然默認(rèn)為10就可以那么為什么還要提供一個(gè)可以指定容量大小的構(gòu)造方法呢?思考下,下面會(huì)說(shuō)到。
四、ArrayList 的常見(jiàn)方法分析
1.add 添加元素

添加的方法,共有四個(gè),下面我們分別分析下其功能源碼:
/**
*添加一個(gè)元素
*/
public boolean add(E e)
{
// 進(jìn)行擴(kuò)容檢查
ensureCapacity(size + 1); // Increments modCount!!
//將e增加至list的數(shù)據(jù)尾部,容量+1
elementData[size++] = e;
return true;
}
/**
*在指定位置添加一個(gè)元素
*/
public void add(int index, E element) {
// 判斷索引是否越界
if (index > size || index < 0)
throw new IndexOutOfBoundsException(
"Index: "+index+", Size: "+size);
// 進(jìn)行擴(kuò)容檢查
ensureCapacity(size+1); // Increments modCount!!
// 對(duì)數(shù)組進(jìn)行復(fù)制處理,目的就是空出index的位置插入element,并將index后的元素位移一個(gè)位置
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
// 將指定的index位置賦值為element
elementData[index] = element;
// list容量+1
size++;
}
/**
*增加一個(gè)集合元素
*/
public boolean addAll(Collection<? extends E> c) {
//將c轉(zhuǎn)換為數(shù)組
Object[] a = c.toArray();
int numNew = a.length;
//擴(kuò)容檢查
ensureCapacity(size + numNew); // Increments modCount
//將c添加至list的數(shù)據(jù)尾部
System.arraycopy(a, 0, elementData, size, numNew);
//更新當(dāng)前容器大小
size += numNew;
return numNew != 0;
}
/**
* 在指定位置,增加一個(gè)集合元素
*/
public boolean addAll(int index, Collection<? extends E> c) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException(
"Index: " + index + ", Size: " + size);
Object[] a = c.toArray();
int numNew = a.length;
ensureCapacity(size + numNew); // Increments modCount
// 計(jì)算需要移動(dòng)的長(zhǎng)度(index之后的元素個(gè)數(shù))
int numMoved = size - index;
// 數(shù)組復(fù)制,空出第index到index+numNum的位置,即將數(shù)組index后的元素向右移動(dòng)numNum個(gè)位置
if (numMoved > 0)
System.arraycopy(elementData, index, elementData, index + numNew,
numMoved);
// 將要插入的集合元素復(fù)制到數(shù)組空出的位置中
System.arraycopy(a, 0, elementData, index, numNew);
size += numNew;
return numNew != 0;
}
/**
* 數(shù)組容量檢查,不夠時(shí)則進(jìn)行擴(kuò)容
*/
public void ensureCapacity( int minCapacity) {
modCount++;
// 當(dāng)前數(shù)組的長(zhǎng)度
int oldCapacity = elementData.length;
// 最小需要的容量大于當(dāng)前數(shù)組的長(zhǎng)度則進(jìn)行擴(kuò)容
if (minCapacity > oldCapacity) {
Object oldData[] = elementData;
// 新擴(kuò)容的數(shù)組長(zhǎng)度為舊容量的1.5倍+1
int newCapacity = (oldCapacity * 3)/2 + 1;
// 如果新擴(kuò)容的數(shù)組長(zhǎng)度還是比最小需要的容量小,則以最小需要的容量為長(zhǎng)度進(jìn)行擴(kuò)容
if (newCapacity < minCapacity)
newCapacity = minCapacity;
// minCapacity is usually close to size, so this is a win:
// 進(jìn)行數(shù)據(jù)拷貝,Arrays.copyOf底層實(shí)現(xiàn)是System.arrayCopy()
elementData = Arrays.copyOf(elementData, newCapacity);
}
}
2.remove 刪除
/**
* 根據(jù)索引位置刪除元素
*/
public E remove(int index) {
// 數(shù)組越界檢查
RangeCheck(index);
modCount++;
// 取出要?jiǎng)h除位置的元素,供返回使用
E oldValue = (E) elementData[index];
// 計(jì)算數(shù)組要復(fù)制的數(shù)量
int numMoved = size - index - 1;
// 數(shù)組復(fù)制,就是將index之后的元素往前移動(dòng)一個(gè)位置
if (numMoved > 0)
System. arraycopy(elementData, index+1, elementData, index,
numMoved);
// 將數(shù)組最后一個(gè)元素置空(因?yàn)閯h除了一個(gè)元素,然后index后面的元素都向前移動(dòng)了,所以最后一個(gè)就沒(méi)用了),好讓gc盡快回收
// 不要忘了size減一
elementData[--size ] = null; // Let gc do its work
return oldValue;
}
/**
* 根據(jù)元素內(nèi)容刪除,只刪除匹配的第一個(gè)
*/
public boolean remove(Object o) {
// 對(duì)要?jiǎng)h除的元素進(jìn)行null判斷
// 對(duì)數(shù)據(jù)元素進(jìn)行遍歷查找,知道找到第一個(gè)要?jiǎng)h除的元素,刪除后進(jìn)行返回,如果要?jiǎng)h除的元素正好是最后一個(gè)那就慘了,時(shí)間復(fù)雜度可達(dá)O(n) 。。。
if (o == null) {
for (int index = 0; index < size; index++)
// null值要用==比較
if (elementData [index] == null) {
fastRemove(index);
return true;
}
} else {
for (int index = 0; index < size; index++)
// 非null當(dāng)然是用equals比較了
if (o.equals(elementData [index])) {
fastRemove(index);
return true;
}
}
return false;
}
/*
* Private remove method that skips bounds checking and does not
* return the value removed.
*/
private void fastRemove(int index) {
modCount++;
// 原理和之前的add一樣,還是進(jìn)行數(shù)組復(fù)制,將index后的元素向前移動(dòng)一個(gè)位置,不細(xì)解釋了,
int numMoved = size - index - 1;
if (numMoved > 0)
System. arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size ] = null; // Let gc do its work
}
/**
* 數(shù)組越界檢查
*/
private void RangeCheck(int index) {
if (index >= size )
throw new IndexOutOfBoundsException(
"Index: "+index+", Size: " +size);
}
分析:
增加和刪除方法到這里就解釋完了,代碼是很簡(jiǎn)單,主要需要特別關(guān)心的就兩個(gè)地方:1.數(shù)組擴(kuò)容,2.數(shù)組復(fù)制,這兩個(gè)操作都是極費(fèi)效率的,最慘的情況下(添加到list第一個(gè)位置,刪除list最后一個(gè)元素或刪除list第一個(gè)索引位置的元素)時(shí)間復(fù)雜度可達(dá)O(n)。
還記得上面那個(gè)坑嗎(為什么提供一個(gè)可以指定容量大小的構(gòu)造方法 )?看到這里是不是有點(diǎn)明白了呢,簡(jiǎn)單解釋下:如果數(shù)組初試容量過(guò)小,假設(shè)默認(rèn)的10個(gè)大小,而我們使用ArrayList的主要操作時(shí)增加元素,不斷的增加,一直增加,不停的增加,會(huì)出現(xiàn)上面后果?那就是數(shù)組容量不斷的受挑釁,數(shù)組需要不斷的進(jìn)行擴(kuò)容,擴(kuò)容的過(guò)程就是數(shù)組拷貝System.arraycopy的過(guò)程,每一次擴(kuò)容就會(huì)開(kāi)辟一塊新的內(nèi)存空間和數(shù)據(jù)的復(fù)制移動(dòng),這樣勢(shì)必對(duì)性能造成影響。那么在這種以寫(xiě)為主(寫(xiě)會(huì)擴(kuò)容,刪不會(huì)縮容)場(chǎng)景下,提前預(yù)知性的設(shè)置一個(gè)大容量,便可減少擴(kuò)容的次數(shù),提高了性能。
數(shù)組擴(kuò)容伴隨著開(kāi)辟新建的內(nèi)存空間以創(chuàng)建新數(shù)組然后進(jìn)行數(shù)據(jù)復(fù)制,而數(shù)組復(fù)制不需要開(kāi)辟新內(nèi)存空間,只需將數(shù)據(jù)進(jìn)行復(fù)制。
上面講增加元素可能會(huì)進(jìn)行擴(kuò)容,而刪除元素卻不會(huì)進(jìn)行縮容,如果在已刪除為主的場(chǎng)景下使用list,一直不停的刪除而很少進(jìn)行增加,那么會(huì)出現(xiàn)什么情況?再或者數(shù)組進(jìn)行一次大擴(kuò)容后,我們后續(xù)只使用了幾個(gè)空間,會(huì)出現(xiàn)上面情況?當(dāng)然是空間浪費(fèi)啦啦啦,怎么辦呢?
/**
* 將底層數(shù)組的容量調(diào)整為當(dāng)前實(shí)際元素的大小,來(lái)釋放空間。
*/
public void trimToSize() {
modCount++;
// 當(dāng)前數(shù)組的容量
int oldCapacity = elementData .length;
// 如果當(dāng)前實(shí)際元素大小 小于 當(dāng)前數(shù)組的容量,則進(jìn)行縮容
if (size < oldCapacity) {
elementData = Arrays.copyOf( elementData, size );
}
3.更新 set
/**
* 將指定位置的元素更新為新元素
*/
public E set( int index, E element) {
// 數(shù)組越界檢查
RangeCheck(index);
// 取出要更新位置的元素,供返回使用
E oldValue = (E) elementData[index];
// 將該位置賦值為行的元素
elementData[index] = element;
// 返回舊元素
return oldValue;
}
4.查找
/**
* 查找指定位置上的元素
*/
public E get( int index) {
RangeCheck(index);
return (E) elementData [index];
}
由于ArrayList使用數(shù)組實(shí)現(xiàn),更新和查找直接基于下標(biāo)操作,變得十分簡(jiǎn)單。
5.是否包含.
/**
* Returns <tt>true</tt> if this list contains the specified element.
* More formally, returns <tt>true</tt> if and only if this list contains
* at least one element <tt>e</tt> such that
* <tt>(o==null ? e==null : o.equals(e))</tt>.
*
* @param o element whose presence in this list is to be tested
* @return <tt> true</tt> if this list contains the specified element
*/
public boolean contains(Object o) {
return indexOf(o) >= 0;
}
/**
* Returns the index of the first occurrence of the specified element
* in this list, or -1 if this list does not contain the element.
* More formally, returns the lowest index <tt>i</tt> such that
* <tt>(o==null ? get(i)==null : o.equals(get(i)))</tt>,
* or -1 if there is no such index.
*/
public int indexOf(Object o) {
if (o == null) {
for (int i = 0; i < size; i++)
if (elementData [i]==null)
return i;
} else {
for (int i = 0; i < size; i++)
if (o.equals(elementData [i]))
return i;
}
return -1;
}
/**
* Returns the index of the last occurrence of the specified element
* in this list, or -1 if this list does not contain the element.
* More formally, returns the highest index <tt>i</tt> such that
* <tt>(o==null ? get(i)==null : o.equals(get(i)))</tt>,
* or -1 if there is no such index.
*/
public int lastIndexOf(Object o) {
if (o == null) {
for (int i = size-1; i >= 0; i--)
if (elementData [i]==null)
return i;
} else {
for (int i = size-1; i >= 0; i--)
if (o.equals(elementData [i]))
return i;
}
return -1;
}
contains主要是檢查indexOf,也就是元素在list中出現(xiàn)的索引位置也就是數(shù)組下標(biāo),再看indexOf和lastIndexOf代碼是不是很熟悉,沒(méi)錯(cuò),和public booleanremove(Object o) 的代碼一樣,都是元素null判斷,都是循環(huán)比較。
6.容量判斷
/**
* Returns the number of elements in this list.
*
* @return the number of elements in this list
*/
public int size() {
return size ;
}
/**
* Returns <tt>true</tt> if this list contains no elements.
*
* @return <tt> true</tt> if this list contains no elements
*/
public boolean isEmpty() {
return size == 0;
}
說(shuō)明:modCount 是干什么的,怎么到處都在操作這個(gè)變量,還有遍歷呢,為啥不講?由于iterator遍歷相對(duì)比較復(fù)雜,而且iterator 是GoF經(jīng)典設(shè)計(jì)模式比較重要的一個(gè),之后會(huì)對(duì)iterator單獨(dú)分析。
五、transient 分析 和 ArrayList的優(yōu)缺點(diǎn)
1.ArrayList的優(yōu)缺點(diǎn)
1、ArrayList底層以數(shù)組實(shí)現(xiàn),是一種隨機(jī)訪問(wèn)模式,再加上它實(shí)現(xiàn)了RandomAccess接口,因此查找也就是get的時(shí)候非常快
2、ArrayList在順序添加一個(gè)元素的時(shí)候非常方便,只是往數(shù)組里面添加了一個(gè)元素而已
不過(guò)ArrayList的缺點(diǎn)也十分明顯:
1、刪除元素的時(shí)候,涉及到一次元素復(fù)制,如果要復(fù)制的元素很多,那么就會(huì)比較耗費(fèi)性能
2、插入元素的時(shí)候,涉及到一次元素復(fù)制,如果要復(fù)制的元素很多,那么就會(huì)比較耗費(fèi)性能
因此,ArrayList比較適合順序添加、隨機(jī)訪問(wèn)的場(chǎng)景。
2.ArrayList和Vector的區(qū)別
ArrayList是線程非安全的,這很明顯,因?yàn)锳rrayList中所有的方法都不是同步的,在并發(fā)下一定會(huì)出現(xiàn)線程安全問(wèn)題。那么我們想要使用ArrayList并且讓它線程安全怎么辦?一個(gè)方法是用Collections.synchronizedList方法把你的ArrayList變成一個(gè)線程安全的List,比如:
List<String> synchronizedList = Collections.synchronizedList(list);
synchronizedList.add("aaa");
synchronizedList.add("bbb");
for (int i = 0; i < synchronizedList.size(); i++)
{
System.out.println(synchronizedList.get(i));
}
另一個(gè)方法就是Vector,它是ArrayList的線程安全版本,其實(shí)現(xiàn)90%和ArrayList都完全一樣,區(qū)別在于:
1、Vector是線程安全的,ArrayList是線程非安全的
2、Vector可以指定增長(zhǎng)因子,如果該增長(zhǎng)因子指定了,那么擴(kuò)容的時(shí)候會(huì)每次新的數(shù)組大小會(huì)在原數(shù)組的大小基礎(chǔ)上加上增長(zhǎng)因子;如果不指定增長(zhǎng)因子,那么就給原數(shù)組大小
3.為什么ArrayList的elementData是用transient修飾的?
private transient Object[] elementData;
為什么elementData是使用transient修飾的呢?我們看一下ArrayList的定義:
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable
看到ArrayList實(shí)現(xiàn)了Serializable接口,這意味著ArrayList是可以被序列化的,用transient修飾elementData意味著我不希望elementData數(shù)組被序列化。這是為什么?因?yàn)樾蛄谢疉rrayList的時(shí)候,ArrayList里面的elementData未必是滿(mǎn)的,比方說(shuō)elementData有10的大小,但是我只用了其中的3個(gè),那么是否有必要序列化整個(gè)elementData呢?顯然沒(méi)有這個(gè)必要,因此ArrayList中重寫(xiě)了writeObject方法:
private void writeObject(java.io.ObjectOutputStream s)
throws java.io.IOException{
// Write out element count, and any hidden stuff
int expectedModCount = modCount;
s.defaultWriteObject();
// Write out array length
s.writeInt(elementData.length);
// Write out all elements in the proper order.
for (int i=0; i<size; i++)
s.writeObject(elementData[i]);
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}
每次序列化的時(shí)候調(diào)用這個(gè)方法,先調(diào)用defaultWriteObject()方法序列化ArrayList中的非transient元素,elementData不去序列化它,然后遍歷elementData,只序列化那些有的元素,這樣:
1、加快了序列化的速度
2、減小了序列化之后的文件大小
六、自己編寫(xiě)一個(gè)MyArrayList
package study.collection;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
public class MyArrayList implements List {
//底層用一個(gè)數(shù)組接收
private Object[] elementData;
//用于記錄集合的元素個(gè)數(shù)
private int size;
/**
* 無(wú)參數(shù)構(gòu)造,默認(rèn)為大小10
*/
public MyArrayList()
{
this(10);
}
/**
* 初始化帶容量的構(gòu)造方法
* @param cap
*/
public MyArrayList(int cap)
{
super();
if(cap < 0)
{
throw new IllegalArgumentException("Illegal cap: "+
cap);
}
elementData = new Object[cap];
}
@Override
public boolean add(Object e) {
//1.添加之前確認(rèn)集合中的大小是夠,因此擴(kuò)容判斷
ensureCapacity(size +1); //添加元素一個(gè)一個(gè)添加,因此size+1;
//2.填充元素
elementData[size++] = e;
return true;
}
/**
* 擴(kuò)容判斷,因?yàn)橹灰砑釉?就需要判斷容器的大小是否滿(mǎn)足
* @param i
*/
private void ensureCapacity(int minCapacity) {
//擴(kuò)容前,需要獲取當(dāng)前的數(shù)組元素的大小
int oldCapacity = elementData.length;
//只有當(dāng)前的容量不滿(mǎn)足,則擴(kuò)容處理
if(oldCapacity < minCapacity)
{
//新大小的比例,采用原來(lái)大小的1.5倍
int newCapacity = (oldCapacity * 3)/2 + 1;
//如果新算出來(lái)的大小不滿(mǎn)足當(dāng)前需要擴(kuò)容的大小,則就以用戶(hù)需要的為主,如果以滿(mǎn)足則以算出來(lái)的最佳大小為主
if(newCapacity < minCapacity)
{
newCapacity = minCapacity;
}
//比例算好后,開(kāi)始執(zhí)行數(shù)組的拷貝操作
Arrays.copyOf(elementData, newCapacity,Object[].class);
}
}
@Override
public void add(int index, Object element) {
//添加指定位置,首先需要做的就是確保索引滿(mǎn)足要求,如果要添加的索引超過(guò)了元素個(gè)數(shù)中的大小
if(index > size || index < 0)
{
throw new IndexOutOfBoundsException(
"Index: "+index+", Size: "+size);
}
//如果沒(méi)有超過(guò),那么就需要開(kāi)始添加元素,這個(gè)時(shí)候同樣需要判斷擴(kuò)容
ensureCapacity(size +1); //添加元素一個(gè)一個(gè)添加,因此size+1;
//接著需要做的事情是需要將原來(lái)位于index 位置的元素,向后面移動(dòng)
//首先判斷,index的后面是否還有元素
int modNum = size - index;
if(modNum > 0)
{
System.arraycopy(elementData, index, elementData, index+1, size - index);
}
//如果沒(méi)有元素或者已經(jīng)拷貝完成,則直接在對(duì)應(yīng)的索引處放置元素即可
elementData[index] = element;
//元素個(gè)數(shù)加+1
size++;
}
@Override
public boolean addAll(Collection c) {
//添加集合元素,首先需要將集合轉(zhuǎn)換為數(shù)組,計(jì)算出數(shù)組的大小
Object[] a = c.toArray();
//計(jì)算出需要的長(zhǎng)度
int numNew = a.length;
//開(kāi)始擴(kuò)容判斷
ensureCapacity(size +numNew); //添加元素的個(gè)數(shù)為numNew
//開(kāi)始數(shù)組拷貝
System.arraycopy(a, 0, elementData, size, numNew);
size += numNew;
return numNew != 0;
}
@Override
public boolean addAll(int index, Collection c) {
//首先索引的正確性
if (index > size || index < 0)
throw new IndexOutOfBoundsException(
"Index: " + index + ", Size: " + size);
//添加的元素集合轉(zhuǎn)換為數(shù)組,計(jì)算要拷貝的長(zhǎng)度,準(zhǔn)備擴(kuò)容
Object[] a = c.toArray();
int numNew = a.length;
ensureCapacity(size + numNew); // Increments modCount
//因?yàn)槭侵付ㄎ恢脭U(kuò)容,因此需要判斷下index后面是否有元素
int numMoved = size - index;
//如果大于0,說(shuō)明要先空出位置來(lái)給a數(shù)組
if(numMoved > 0)
{
System.arraycopy(elementData, index, elementData, index+1, size-index);
}
//空出為位置后,然后將集合的元素放到空出的位置上面
System.arraycopy(a, 0, elementData,index, numNew);
size += numNew;
return numNew != 0;
}
@Override
public void clear() {
for (int i = 0; i < size; i++)
elementData[i] = null;
size = 0;
}
@Override
public boolean contains(Object o) {
return indexOf(o) >= 0;
}
@Override
public boolean containsAll(Collection c) {
//迭代器暫不去實(shí)現(xiàn)
return false;
}
@Override
public Object get(int index) {
//對(duì)于數(shù)組而言,根據(jù)索引獲取元素非常簡(jiǎn)單,但需要先檢查inde的合法性,避免越界
RangeCheck(index);
return elementData[index];
}
private void RangeCheck(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(
"Index: "+index+", Size: "+size);
}
@Override
public int indexOf(Object o) {
//循環(huán)遍歷,找出元素,注意是equals比較
if (o == null) {
for (int i = 0; i < size; i++)
if (elementData[i]==null)
return i;
} else {
for (int i = 0; i < size; i++)
if (o.equals(elementData[i]))
return i;
}
return -1;
}
@Override
public boolean isEmpty() {
return size == 0;
}
@Override
public Iterator iterator() {
//涉及迭代器,暫不去關(guān)注
return null;
}
@Override
public int lastIndexOf(Object o) {
//反向獲取,則反向循環(huán)
if (o == null) {
for (int i = size-1; i >= 0; i--)
if (elementData[i]==null)
return i;
} else {
for (int i = size-1; i >= 0; i--)
if (o.equals(elementData[i]))
return i;
}
return -1;
}
@Override
public ListIterator listIterator() {
//涉及迭代器,暫不去關(guān)注
return null;
}
@Override
public ListIterator listIterator(int index) {
//涉及迭代器,暫不去關(guān)注
return null;
}
@Override
public boolean remove(Object o) {
if (o == null) {
for (int index = 0; index < size; index++)
if (elementData[index] == null) {
fastRemove(index);
return true;
}
} else {
for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) {
fastRemove(index);
return true;
}
}
return false;
}
//找到指定的索引開(kāi)始刪除
private void fastRemove(int index) {
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null;
}
@Override
public Object remove(int index) {
//合法性檢查
RangeCheck(index);
//取出原來(lái)老的元素,以便返回
Object oldValue = elementData[index];
//需要開(kāi)始拷貝數(shù)組,因?yàn)閯h除了索引處的元素,那么則需要向前移動(dòng)元素
//需要看后面有沒(méi)有移動(dòng)的元素,-1 是減去當(dāng)前這個(gè)刪除的元素
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);//從index+1 開(kāi)始拷貝到 index 處
elementData[--size] = null; //元素個(gè)數(shù)減去一,同事最后一個(gè)位置置空,等待垃圾回收
return oldValue;
}
@Override
public boolean removeAll(Collection c) {
////涉及迭代器,暫不去關(guān)注
return false;
}
@Override
public boolean retainAll(Collection c) {
////涉及迭代器,暫不去關(guān)注
return false;
}
@Override
public Object set(int index, Object element) {
RangeCheck(index);
Object oldValue = elementData[index];
elementData[index] = element;
return oldValue;
}
@Override
public int size() {
return size;
}
@Override
public List subList(int fromIndex, int toIndex) {
////涉及迭代器,暫不去關(guān)注
return null;
}
@Override
public Object[] toArray() {
return Arrays.copyOf(elementData, size);
}
@Override
public Object[] toArray(Object[] a) {
if (a.length < size)
// Make a new array of a's runtime type, but my contents:
return Arrays.copyOf(elementData, size, a.getClass());
System.arraycopy(elementData, 0, a, 0, size);
if (a.length > size)
a[size] = null;
return a;
}
//測(cè)試
public static void main(String[] args)
{
MyArrayList list = new MyArrayList();
list.add("333");
list.add("444");
list.add("5");
list.add("344433");
list.add("333");
list.add("333");
System.out.println(list.size());
// System.out.println(list.get(6));
list.remove("444");
System.out.println(list.size());
}
}
說(shuō)明:其他關(guān)于JDK1.6 和 1.7 和 1.8 的區(qū)別可以看:
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- 對(duì)Java ArrayList的自動(dòng)擴(kuò)容機(jī)制示例講解
- Java ArrayList的底層實(shí)現(xiàn)方法
- Java容器ArrayList知識(shí)點(diǎn)總結(jié)
- Java ArrayList擴(kuò)容問(wèn)題實(shí)例詳解
- Java針對(duì)ArrayList自定義排序的2種實(shí)現(xiàn)方法
- java實(shí)現(xiàn)ArrayList根據(jù)存儲(chǔ)對(duì)象排序功能示例
- java 集合之實(shí)現(xiàn)類(lèi)ArrayList和LinkedList的方法
- Java中Arraylist動(dòng)態(tài)擴(kuò)容方法詳解
- Java中ArrayList的removeAll方法詳解
- java并發(fā)容器CopyOnWriteArrayList實(shí)現(xiàn)原理及源碼分析
- Java ArrayList add(int index, E element)和set(int index, E element)兩個(gè)方法的說(shuō)明
相關(guān)文章
SpringCloud手寫(xiě)Ribbon實(shí)現(xiàn)負(fù)載均衡
這篇文章主要介紹了SpringCloud手寫(xiě)Ribbon實(shí)現(xiàn)負(fù)載均衡的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-01-01
Spring AOP與代理類(lèi)的執(zhí)行順序級(jí)別淺析
這篇文章主要介紹了Spring AOP與代理類(lèi)的執(zhí)行順序級(jí)別,關(guān)于 Spring AOP和Aspectj的關(guān)系,兩個(gè)都實(shí)現(xiàn)了切面編程,Spring AOP更多地是為了Spring框架本身服務(wù)的,而Aspectj具有更強(qiáng)大、更完善的切面功能2023-03-03
java 用redisTemplate 的 Operations存取list集合操作
這篇文章主要介紹了java 用redisTemplate 的 Operations存取list集合操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-08-08
Java中文件創(chuàng)建于寫(xiě)入內(nèi)容的常見(jiàn)方法
在日常開(kāi)發(fā)中,肯定離不開(kāi)要和文件打交道,今天就簡(jiǎn)單羅列一下平時(shí)比較常用的創(chuàng)建文件并向文件中寫(xiě)入數(shù)據(jù)的幾種方式,希望對(duì)大家有一定的幫助2023-10-10
如何基于spring security實(shí)現(xiàn)在線用戶(hù)統(tǒng)計(jì)
這篇文章主要介紹了如何基于spring security實(shí)現(xiàn)在線用戶(hù)統(tǒng)計(jì),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-06-06
Maven和IntelliJ IDEA搭建多模塊微服務(wù)的實(shí)現(xiàn)
本文主要介紹了Maven和IntelliJ IDEA搭建多模塊微服務(wù)的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2024-05-05

