ArrayList

各種原因,前兩年做C語言去了,現(xiàn)在重新做JAVA, 感覺自己基礎(chǔ)很不扎實,要好好學(xué)習(xí)啦, 先從簡單的開始~

以下內(nèi)容基于jdk1.7.0_79源碼;

什么是ArrayList

可以簡單的認為是一個動態(tài)數(shù)組;實際上ArrayList就是用數(shù)組實現(xiàn)的,長度不夠時,調(diào)用Arrays.copyOf方法,拷貝當(dāng)前數(shù)組到一個新的長度更大的數(shù)組;

ArrayList特點

隨機訪問速度快,插入和移除性能較差(數(shù)組的特點);

支持null元素;

有順序;

元素可以重復(fù);

線程不安全;

ArrayList繼承的類和實現(xiàn)的接口

如下圖,是與ArrayList相關(guān)的接口和類,下面將一一介紹各個接口和類中的方法;

PS:ArrayList中的方法主要是由Collection接口和List接口定義的;

Iterable接口

實現(xiàn)此接口以便支持foreach語法,如下代碼,ArrayList可以直接使用foreach語句遍歷元素:

View Code

Collection接口

int size()方法:

返回集合的大小,在ArrayList類中有一個int類型的size私有屬性,當(dāng)調(diào)用size方法時,直接返回該屬性;

boolean isEmpty()方法:

判斷集合是否為空,在ArrayList中,通過判斷size == 0來判斷集合是否為空;

boolean contains(Object o)方法:

判斷集合是否含有對象o,在ArrayList中,通過判斷indexOf(o) >= 0來判斷是否含有o對象;

查看indexOf(o)方法,代碼如下,主要功能是返回元素第一次出現(xiàn)時的下標(biāo)索引,所以當(dāng)下標(biāo)索引大于等于0時,表示集合中存在該元素:

復(fù)制代碼
? ? 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;
? ? }
復(fù)制代碼

注意這里的相等判斷,調(diào)用的是o對象的equals方法,所以在調(diào)用contains方法時,要特別關(guān)注集合中對象的equals方法,是否有被重寫過,如Integer、String的equals方法是被重寫過的,一般我們自己定義的對象,如果沒重寫equals的話,默認調(diào)用的是Object的equals方法,舉個例子,看一下就明白了:

復(fù)制代碼
package com.pichen.basis.col;

import java.util.ArrayList;
import java.util.List;

class Dog{
}
public class ContainsTest {

? ? public static void main(String[] args) {
? ? ? ? List<Dog> dogList = new ArrayList<Dog>();
? ? ? ? Dog dog1 = new Dog();
? ? ? ? Dog dog2 = new Dog();
? ? ? ? dogList.add(dog1);
? ? ? ? System.out.println(dogList.contains(dog2));//false
? ? ? ? 
? ? ? ? List<String> strList = new ArrayList<String>(); 
? ? ? ? strList.add("teststr");
? ? ? ? 
? ? ? ? String str = new String("teststr");
? ? ? ? System.out.println(strList.contains(str));//true
? ? ? ? 
? ? }

}
復(fù)制代碼

Iterator<E> iterator()方法:

返回一個迭代器對象,用于遍歷集合,事實上,ArrayList類里有兩個內(nèi)部類ArrayList.Itr和ArrayList.ListItr,分別對應(yīng)Iterator迭代器和ListIterator迭代器,后者比前者功能更加強大;從ArrayList.ListItr繼承自ArrayList.Itr就可以看出來,ListIterator迭代器支持更多的操作,如判斷前面還有沒有元素,即hasPrevious()方法,等;

Object[] toArray()方法:

將集合ArrayList轉(zhuǎn)換成Object數(shù)組,有時候需要用到數(shù)組的一些api時,可以使用該方法,注意返回的結(jié)果是Object類型的數(shù)組,如果想返回指定類型的數(shù)組,可以使用以下方法,<T> T[] toArray(T[] a);

<T> T[] toArray(T[] a)方法:

集合轉(zhuǎn)數(shù)組,返回指定類型的數(shù)組,注意入?yún)[] a需要指定數(shù)組存儲的空間,返回值為指定類型的數(shù)組;舉個例子,假如有一個Integer類型的集合,如果想把它轉(zhuǎn)換成Integer類型的數(shù)組,可以這樣寫:Integer[] arr = list.toArray(new Integer[list.size()]);

boolean add(E e)方法:

在集合最后面增加一個元素,在ArrayList中,其實現(xiàn)就是在其內(nèi)部數(shù)組后面增加一個元素,不過要先保證內(nèi)部數(shù)組長度足夠,如下代碼:

? ? public boolean add(E e) {
? ? ? ? ensureCapacityInternal(size + 1);? // Increments modCount!!
? ? ? ? elementData[size++] = e;
? ? ? ? return true;
? ? }

boolean remove(Object o)方法:

在集合中移除對象o,在ArrayList中,其實現(xiàn)較add方法復(fù)雜,涉及空對象判斷,equals比較,數(shù)組移動等,性能相對較差;

boolean containsAll(Collection<?> c)方法:

判斷是否包含集合c中的所有元素,在ArrayList中,其實現(xiàn)方法是遍歷集合c中的每一個元素,調(diào)用contains方法,判斷集合是否包含該元素,只要有一個不包含就返回false,如下代碼:

? ? public boolean containsAll(Collection<?> c) {
? ? ? ? for (Object e : c)
? ? ? ? ? ? if (!contains(e))
? ? ? ? ? ? ? ? return false;
? ? ? ? return true;
? ? }

boolean addAll(Collection<? extends E> c)方法:

將集合c中的所有元素加到目標(biāo)集合中去,在ArrayList中,其實現(xiàn)是先將集合c轉(zhuǎn)換成數(shù)組,然后通過數(shù)組拷貝實現(xiàn);

boolean removeAll(Collection<?> c)方法:

移除目標(biāo)集合中含有‘集合c中元素’的所有元素,在ArrayList中,最終還是操作數(shù)組,性能相對較差;

boolean retainAll(Collection<?> c)方法:

移除目標(biāo)集合中‘不包含集合c中元素’的所有元素,在ArrayList中removeAll方法和retainAll方法都是通過調(diào)用ArrayList的batchRemove方法來實現(xiàn)的,后續(xù)詳細了解該方法的實現(xiàn);

void clear()方法:

移除目標(biāo)集合中的所有元素,在ArrayList中,就是將其內(nèi)部數(shù)組所有元素賦null;

boolean equals(Object o)和int hashCode()方法

在ArrayLisy中,上面兩個方法都被重寫,equals方法依次取出集合中的所有元素進行比較,通過元素的equals方法,判斷是否相等,全部相等返回true;

hashCode方法的計算是通過所有元素的hashCode計算得到;順便說下hashcode,在java中隨處可見,一般用在HashMap, Hashtable, HashSet等等中,可用于減少equals方法的調(diào)用,快速訪問元素等,其實就是散列表的概念,如比較元素先比較其hashcode,如果hashcode不相等,那么這兩個元素肯定不相等,也就不用調(diào)用其equals方法了;

demo代碼:以上方法的簡單使用

復(fù)制代碼
package com.pichen.basis.col;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

public class Test {

? ? public static void main(String[] args) {
? ? ? ? List<Integer> list = new ArrayList<Integer>();
? ? ? ? for(int i = 0; i < 10; i++){
? ? ? ? ? ? list.add(i);
? ? ? ? }
? ? ? ? 
? ? ? ? //直接打印
? ? ? ? System.out.println(list.toString());
? ? ? ? 
? ? ? ? //size()
? ? ? ? System.out.println(list.size());
? ? ? ? 
? ? ? ? //contains
? ? ? ? System.out.println(list.contains(2));
? ? ? ? 
? ? ? ? //iterator
? ? ? ? Iterator<Integer> iterator = list.iterator();
? ? ? ? while(iterator.hasNext()){
? ? ? ? ? ? System.out.print(iterator.next() + " ");
? ? ? ? }
? ? ? ? 
? ? ? ? //toArray
? ? ? ? Object[] objArr = list.toArray();
? ? ? ? Integer[] intArr = list.toArray(new Integer[list.size()]);
? ? ? ? System.out.println("\n" + list.toArray());
? ? ? ? 
? ? ? ? //add
? ? ? ? list.add(5);
? ? ? ? 
? ? ? ? //remove
? ? ? ? list.remove(5);
? ? ? ? 
? ? ? ? System.out.println(list);
? ? ? ? 
? ? ? ? //containsAll
? ? ? ? System.out.println(list.containsAll(Arrays.asList(5,6)));
? ? ? ? 
? ? ? ? //addAll
? ? ? ? list.addAll(Arrays.asList(555,666));
? ? ? ? System.out.println(list);
? ? ? ? 
? ? ? ? //removeAll
? ? ? ? list.removeAll(Arrays.asList(555,666));
? ? ? ? System.out.println(list);
? ? ? ? 
? ? ? ? //clear
? ? ? ? list.clear();
? ? ? ? System.out.println(list);
? ? ? ? 
? ? }
}
復(fù)制代碼

List接口

除了Collection中定義的方法為,該接口增加了以下方法

boolean addAll(int index, Collection<? extends E> c);

在ArrayList中,該方法是在指定位置處增加一個集合中的所有元素,該操作涉及數(shù)組移動;

E get(int index);

返回下標(biāo)為index的元素;

E set(int index, E element);

改變下標(biāo)為index的元素的值

void add(int index, E element);

在下標(biāo)為index的地方插入元素element,該操作涉及數(shù)組移動;

E remove(int index);

移除下標(biāo)為index的元素,該操作涉及數(shù)組移動;

int indexOf(Object o);

返回元素o的最小下標(biāo),通過調(diào)用o的equals方法與集合中的元素進行比較;

int lastIndexOf(Object o);

返回元素o的最大下標(biāo),通過調(diào)用o的equals方法與集合中的元素進行比較;

ListIterator<E> listIterator();

返回listIterator迭代器,該迭代器支持向前操作;

ListIterator<E> listIterator(int index);

返回listIterator迭代器,從特定的位置開始,該迭代器支持向前操作;

List<E> subList(int fromIndex, int toIndex);

返回下標(biāo)在fromIndex和toIndex之間的元素集合;

demo代碼:以上方法的簡單使用:

View Code

RandomAccess, Cloneable, java.io.Serializable接口

這三個接口是標(biāo)識接口,里面都是空的;

RandomAccess標(biāo)識其支持快速隨機訪問;

Cloneable標(biāo)識其支持對象復(fù)制;

Serializable標(biāo)識其可序列化;

AbstractCollection類

大部分方法前面已經(jīng)說明過了,不過該類下的contains方法、toArray方法等,遍歷的時候都是使用更加通用的迭代器方式進行遍歷;

AbstractList類

大部分方法前面已經(jīng)說明過了,不過該類中有兩個私有內(nèi)部類Itr和ListItr,對應(yīng)的分別是兩個迭代器;

ArrayList類

ArrayList的具體實現(xiàn)

成員屬性:

private static final int DEFAULT_CAPACITY = 10;//初始容量

private static final Object[] EMPTY_ELEMENTDATA = {};//空ArrayList實例共享的一個空數(shù)組

private transient Object[] elementData; //真正存儲ArrayList中的元素的數(shù)組;

private int size;//存儲ArrayList的大小,注意不是elementData的長度;

除了其父接口定義的方法外,該類增加了以下方法

public ArrayList(int initialCapacity):

構(gòu)造函數(shù),指定初始大小

public ArrayList()

構(gòu)造函數(shù),使用共享的EMPTY_ELEMENTDATA空數(shù)組

public ArrayList(Collection<? extends E> c)

構(gòu)造函數(shù),通過集合初始化ArrayList

public void trimToSize()

節(jié)省空間用的,ArrayList是通過數(shù)組實現(xiàn)的,大小不夠時,增加數(shù)組長度,有可能出現(xiàn)數(shù)組長度大于ArrayList的size情況;

public void ensureCapacity(int minCapacity)

保證ArrayList能容納minCapacity個元素;

私有方法

ArrayList內(nèi)部數(shù)組擴容

查看ArrayList的add源碼方法,如下:

? ? public boolean add(E e) {
? ? ? ? ensureCapacityInternal(size + 1);? // Increments modCount!!
? ? ? ? elementData[size++] = e;
? ? ? ? return true;
? ? }

在往ArrayList增加e對象前,要先保證內(nèi)部數(shù)組空間足夠,即調(diào)用ensureCapacityInternal判斷,查看其代碼,如下:

復(fù)制代碼
? ? private void ensureCapacityInternal(int minCapacity) {
? ? ? ? if (elementData == EMPTY_ELEMENTDATA) {
? ? ? ? ? ? minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
? ? ? ? }

? ? ? ? ensureExplicitCapacity(minCapacity);
? ? }
復(fù)制代碼

入?yún)inCapacity為ArrayList要保證的最小空間大小,首先判斷內(nèi)部數(shù)組elementData是否是初始的空數(shù)組(當(dāng)ArrayList是空實例的情況),如果是的話,且minCapacity比DEFAULT_CAPACITY小,則設(shè)minCapacity為默認的初始容量大小DEFAULT_CAPACITY;接著調(diào)用ensureExplicitCapacity方法,查看其代碼,如下:

復(fù)制代碼
? ? private void ensureExplicitCapacity(int minCapacity) {
? ? ? ? modCount++;

? ? ? ? // overflow-conscious code
? ? ? ? if (minCapacity - elementData.length > 0)
? ? ? ? ? ? grow(minCapacity);
? ? }
復(fù)制代碼

首先,是對修改次數(shù)modCount加一,這里的modCount給ArrayList的迭代器使用的,在并發(fā)操作被修改時,提供快速失敗行為(保證modCount在迭代期間不變,否則拋出ConcurrentModificationException異常,可以查看源碼859行),接著判斷minCapacity是否大于當(dāng)前ArrayList內(nèi)部數(shù)組長度,大于的話調(diào)用grow方法對內(nèi)部數(shù)組elementData擴容,grow方法代碼如下:

復(fù)制代碼
? ? 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);
? ? }
復(fù)制代碼

首先,獲取內(nèi)部數(shù)組elementData長度,并對它的大小增加1.5倍(oldCapacity + (oldCapacity >> 1)),賦給newCapacity,當(dāng)newCapacity仍然比minCapacity(要保證的最小空間大?。┬〉臅r候,直接讓newCapacity 等于minCapacity;然后再判斷newCapacity 是不是大于MAX_ARRAY_SIZE,如果大于的話,調(diào)用hugeCapacity方法,處理大容量空間的情況,該方法判斷是否溢出(拋出OutOfMemoryError),以及當(dāng)minCapacity大于MAX_ARRAY_SIZE時,直接返回Integer.MAX_VALUE;最后就是真正地數(shù)組擴容了,調(diào)用Arrays.copyOf方法,拷貝舊的內(nèi)部數(shù)組內(nèi)容到一個新的(長度為newCapacity)的數(shù)組,并更改內(nèi)部數(shù)組elementData的引用為新數(shù)組;

ArrayList遍歷

一般工作中用的比較多的是遍歷操作,ArrayList支持三種方式

  • for循環(huán)下標(biāo)遍歷;
  • 迭代器(Iterator和ListIterator);
  • foreach語句。
復(fù)制代碼
package com.pichen.basis.col;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;

public class Test {

? ? public static void main(String[] args) {
? ? ? ? List<Integer> list = new ArrayList<Integer>();
? ? ? ? for(int i = 0; i < 10; i++){
? ? ? ? ? ? list.add(i);
? ? ? ? }
? ? ? ? 
? ? ? ? //直接打印
? ? ? ? System.out.println(list.toString());
? ? ? ? 
? ? ? ? //for循環(huán)
? ? ? ? System.out.println("for循環(huán):");
? ? ? ? for(int i = 0; i < list.size(); i++){
? ? ? ? ? ? System.out.print(list.get(i) + " ");
? ? ? ? }
? ? ? ? 
? ? ? ? //foreach
? ? ? ? System.out.println("\nforeach:");
? ? ? ? for(Integer i : list){
? ? ? ? ? ? System.out.print(i + " ");
? ? ? ? }
? ? ? ? 
? ? ? ? //iterator
? ? ? ? System.out.println("\niterator:");
? ? ? ? Iterator<Integer> iterator = list.iterator();
? ? ? ? while(iterator.hasNext()){
? ? ? ? ? ? System.out.print(iterator.next() + " ");
? ? ? ? }
? ? ? ? 
? ? ? ? //listIterator
? ? ? ? System.out.println("\nlistIterator:");
? ? ? ? ListIterator<Integer> listIterator = list.listIterator();
? ? ? ? while(listIterator.hasNext()){
? ? ? ? ? ? System.out.print(listIterator.next() + " ");
? ? ? ? }
? ? ? ? System.out.println();
? ? ? ? while(listIterator.hasPrevious()){
? ? ? ? ? ? System.out.print(listIterator.previous() + " ");
? ? ? ? }
? ? }
}
復(fù)制代碼


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容