1、ArrayList深入理解實(shí)現(xiàn)原理

image.png
ArrayList源碼分析主要由五部分組成,一是繼承和實(shí)現(xiàn)類,二是基本屬性,三是構(gòu)造方法,四是主要方法,五是分析與總結(jié)。

一、 ArrayList概述:

ArrayList特點(diǎn):是基于數(shù)組實(shí)現(xiàn)的動(dòng)態(tài)數(shù)組,其容量能自動(dòng)增長,元素有順序、可重復(fù) 、查詢快、增刪慢、線程不安全,內(nèi)部的元素可以直接通過get與set方法進(jìn)行訪問。

ArrayList繼承了AbstractList,實(shí)現(xiàn)了RandomAccess、Cloneable和Serializable接口!

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable

(1)繼承AbstractList,把模板類定義成抽象類,抽象類本身不能實(shí)例化,繼承接口的實(shí)現(xiàn)類必須要實(shí)現(xiàn)接口的所有方法,而模板類僅僅是為了實(shí)現(xiàn)一些通用方法不需要實(shí)現(xiàn)所有接口方法,不會(huì)產(chǎn)生沒有實(shí)現(xiàn)的接口濫用。實(shí)現(xiàn)了代碼的最大化復(fù)用和代碼的最大化精簡(jiǎn)。
AbstractList又繼承了AbstractCollection實(shí)現(xiàn)了List接口,List提供了相關(guān)的添加、刪除、修改、遍歷等功能!

public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> {
    protected AbstractList() {
    }

(2)實(shí)現(xiàn)了List接口,表明ArrayList是一個(gè)有序、可重復(fù)的數(shù)組,提供了相關(guān)的添加、刪除、修改、遍歷等功能!

(3)實(shí)現(xiàn)RandomAccess接口,提供了隨機(jī)訪問功能,是一個(gè)標(biāo)記接口,實(shí)際上就是通過下標(biāo)序號(hào)進(jìn)行快速訪問,主要目的是使算法能夠在隨機(jī)和順序訪問的list中表現(xiàn)的更加高效。
Collections下的binarySearch方法的源碼:

public static <T>int binarySearch(List<? extends Comparable<? super T>> list, T key) {
    if (list instanceof RandomAccess || list.size()<BINARYSEARCH_THRESHOLD)
        return Collections.indexedBinarySearch(list, key);
    else
        return Collections.iteratorBinarySearch(list, key);
}

indexedBinarySearch是直接通過get訪問數(shù)據(jù)
iteratorBinarySearch是通過ListIterator查找響應(yīng)的元素

實(shí)現(xiàn)RandomAccess接口的的List可以通過簡(jiǎn)單的for循環(huán)來訪問數(shù)據(jù)比使用iterator訪問來的高效快速。

(4)實(shí)現(xiàn)了Cloneable接口,表明ArrayList支持克隆,即可以重寫Object.clone方法,否則在重寫clone方法時(shí),報(bào)CloneNotSupportedException
(5)實(shí)現(xiàn)了Serializable接口可以被序列化與反序列化。

二、ArrayList的繼承關(guān)系

java.lang.Object 
    java.util.AbstractCollection<E> 
        java.util.AbstractList<E> 
            java.util.ArrayList<E> 
 
All Implemented Interfaces: 
Serializable, Cloneable, Iterable<E>, Collection<E>, List<E>, RandomAccess 
Direct Known Subclasses: 
AttributeList, RoleList, RoleUnresolvedList

類中屬性

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
    // 版本號(hào)
    private static final long serialVersionUID = 8683452581122892189L;
    // 缺省容量
    private static final int DEFAULT_CAPACITY = 10;
    // 空對(duì)象數(shù)組
    private static final Object[] EMPTY_ELEMENTDATA = {};
    // 缺省空對(duì)象數(shù)組
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
    // 元素?cái)?shù)組
    transient Object[] elementData;
    // 實(shí)際元素大小,默認(rèn)為0
    private int size;
    // 最大數(shù)組容量
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
}

三、ArrayList方法(API)

// Collection中定義的API
boolean             add(E object)
boolean             addAll(Collection<? extends E> collection)
void                clear()
boolean             contains(Object object)
boolean             containsAll(Collection<?> collection)
boolean             equals(Object object)
int                 hashCode()
boolean             isEmpty()
Iterator<E>         iterator()
boolean             remove(Object object)
boolean             removeAll(Collection<?> collection)
boolean             retainAll(Collection<?> collection)
int                 size()
<T> T[]             toArray(T[] array)
Object[]            toArray()
// AbstractCollection中定義的API
void                add(int location, E object)
boolean             addAll(int location, Collection<? extends E> collection)
E                   get(int location)
int                 indexOf(Object object)
int                 lastIndexOf(Object object)
ListIterator<E>     listIterator(int location)
ListIterator<E>     listIterator()
E                   remove(int location)
E                   set(int location, E object)
List<E>             subList(int start, int end)
// ArrayList新增的API
Object               clone()
void                 ensureCapacity(int minimumCapacity)
void                 trimToSize()
void                 removeRange(int fromIndex, int toIndex)

四、構(gòu)造方法(有3個(gè))

(1)無參構(gòu)造,初始化長度為0的數(shù)組

public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

(2)傳入int類型的值,初始化長度為自定義的數(shù)組

public ArrayList(int initialCapacity) {
    if (initialCapacity > 0) {
        this.elementData = new Object[initialCapacity];
    } else if (initialCapacity == 0) {
        this.elementData = EMPTY_ELEMENTDATA;
    } else {
        throw new IllegalArgumentException("Illegal Capacity: "+
                                           initialCapacity);
    }
}

(3)對(duì)傳入的集合元素進(jìn)行復(fù)制,構(gòu)造一個(gè)包含指定元素的列表

public ArrayList(Collection<? extends E> c) {
    elementData = c.toArray();
    if ((size = elementData.length) != 0) {
        // c.toArray might (incorrectly) not return Object[] (see 6260652)
        if (elementData.getClass() != Object[].class)
            elementData = Arrays.copyOf(elementData, size, Object[].class);
    } else {
        // replace with empty array.
        this.elementData = EMPTY_ELEMENTDATA;
    }
}

五、 主要方法解析

1. add(E e),在元素列表的末尾插入指定的元素,返回true或者拋出異常,無其他返回值

每次添加元素之前都會(huì)判斷添加后的容量是否需要擴(kuò)容

//添加單個(gè)元素
    public boolean add(E e) {
        //判斷添加后的長度是否需要擴(kuò)容
        ensureCapacityInternal(size + 1); 
        //在數(shù)組末尾添加上當(dāng)前元素,并且修改size大小
        elementData[size++] = e;
        return true;
    }

看到它首先調(diào)用了ensureCapacityInternal()方法.注意參數(shù)是size+1,這是個(gè)面試考點(diǎn)

//集合的初始容量為10
    private static final int DEFAULT_CAPACITY = 10;
    //判斷是否是第一次初始化數(shù)組
    private void ensureCapacityInternal(int minCapacity) {
/**
EMPTY_ELEMENTDATA是elementData 的初始化{}一個(gè)空數(shù)組,elementData是數(shù)組緩沖器,
ArrayList的元素都放在這個(gè)數(shù)組緩沖器中。
DEFAULT_CAPACITY是數(shù)組列表初始容量大小為10,ArrayList會(huì)在第一次添加元素的時(shí)候設(shè)置數(shù)組緩沖
器的初始大小為10.
**/

        //判斷當(dāng)前數(shù)組是否 == EMPTY_ELEMENTDATA,因?yàn)槟J(rèn)構(gòu)造函數(shù)創(chuàng)建時(shí)是將空數(shù)組EMPTY_ELEMENTDATA賦值給elementData
        if (elementData == EMPTY_ELEMENTDATA) {
            //判斷默認(rèn)容量10和當(dāng)前數(shù)據(jù)長度的大小,取其中大的值作為判斷本次是否需要擴(kuò)容的依據(jù),由于第一次數(shù)組是空的,所以默認(rèn)要使數(shù)組擴(kuò)容到10的長度
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        //判斷是否需要擴(kuò)容
        ensureExplicitCapacity(minCapacity);
    }

這個(gè)方法里又嵌套調(diào)用了兩個(gè)方法:計(jì)算容量+確保容量
計(jì)算容量:如果elementData是空,則返回默認(rèn)容量10和size+1的最大值,否則返回size+1

計(jì)算完容量后,進(jìn)行確保容量可用:(modCount不用理它,它用來計(jì)算修改次數(shù))
如果size+1 > elementData.length證明數(shù)組已經(jīng)放滿,則增加容量,調(diào)用grow()。

//判斷擴(kuò)容的方法
    private void ensureExplicitCapacity(int minCapacity) {
        //如果需要擴(kuò)容modCount++,此參數(shù)是指當(dāng)前列表結(jié)構(gòu)被修改的次數(shù)
        modCount++;
        // 判斷當(dāng)前數(shù)據(jù)量是否大于數(shù)組的長度
        if (minCapacity - elementData.length > 0)
            //如果大于則進(jìn)行擴(kuò)容操作
            grow(minCapacity);
    }

增加容量:默認(rèn)1.5倍擴(kuò)容。
獲取當(dāng)前數(shù)組長度=>oldCapacity
oldCapacity>>1 表示將oldCapacity右移一位(位運(yùn)算),相當(dāng)于除2。再加上1,相當(dāng)于新容量擴(kuò)容1.5倍。
如果newCapacity>1=1,1<2所以如果不處理該情況,擴(kuò)容將不能正確完成。
如果新容量比最大值還要大,則將新容量賦值為VM要求最大值。
將elementData拷貝到一個(gè)新的容量中。

// 內(nèi)部數(shù)組的容量可以擴(kuò)展到的最大值
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

//擴(kuò)容方法
    private void grow(int minCapacity) {
        // 記錄擴(kuò)容前數(shù)組的長度
        int oldCapacity = elementData.length;
        //將原數(shù)組的長度擴(kuò)大0.5倍作為擴(kuò)容后新數(shù)組的長度(如果擴(kuò)容前數(shù)組長度為10,那么經(jīng)過擴(kuò)容后的數(shù)組長度應(yīng)該為15)
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        //如果擴(kuò)容后的長度小于當(dāng)前數(shù)據(jù)量,那么就將當(dāng)前數(shù)據(jù)量的長度作為本次擴(kuò)容的長度
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        //判斷新數(shù)組長度是否大于可分配數(shù)組的最大大小
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            //將擴(kuò)容長度設(shè)置為最大可用長度
            newCapacity = hugeCapacity(minCapacity);
        // 拷貝,擴(kuò)容,構(gòu)建一個(gè)新的數(shù)組
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

//判斷如果新數(shù)組長度超過當(dāng)前數(shù)組定義的最大長度時(shí),就將擴(kuò)容長度設(shè)置為Interger.MAX_VALUE,也就是int的最大長度
    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE : MAX_ARRAY_SIZE;
        }

public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
        T[] copy = ((Object)newType == (Object)Object[].class)
            ? (T[]) new Object[newLength]
            : (T[]) Array.newInstance(newType.getComponentType(), newLength);
        System.arraycopy(original, 0, copy, 0,
                         Math.min(original.length, newLength));
        return copy;
    }

size+1的問題

好了,那到這里可以說一下為什么要size+1。
size+1代表的含義是:
如果集合添加元素成功后,集合中的實(shí)際元素個(gè)數(shù)。
為了確保擴(kuò)容不會(huì)出現(xiàn)錯(cuò)誤。
假如不加一處理,如果默認(rèn)size是0,則0+0>>1還是0。
如果size是1,則1+1>>1還是1。有人問:不是默認(rèn)容量大小是10嗎?事實(shí)上,jdk1.8版本以后,ArrayList的擴(kuò)容放在add()方法中。之前放在構(gòu)造方法中。我用的是1.8版本,所以默認(rèn)ArrayList arrayList = new ArrayList();后,size應(yīng)該是0.所以,size+1對(duì)擴(kuò)容來講很必要.

2. add(int index,E element),在指定位置添加元素

// 在指定位置添加元素
public void add(int index, E element) {
    // 檢測(cè)指定位置下標(biāo)是否合法,是否在列表的長度范圍內(nèi)
    rangeCheckForAdd(index);
    // 擴(kuò)展內(nèi)部數(shù)組的容量
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    // 分片段拷貝數(shù)組到新的列表
    System.arraycopy(elementData, index, elementData, index + 1,
            size - index);
    // 指定下標(biāo)的數(shù)組值為當(dāng)前添加的元素
    elementData[index] = element;
    // 長度自增長
    size++;
}
public static native void arraycopy(Object src,  int  srcPos,
                                        Object dest, int destPos,
                                        int length);

代碼解釋:
Object src : 原數(shù)組
int srcPos : 從元數(shù)據(jù)的起始位置開始
Object dest : 目標(biāo)數(shù)組
int destPos : 目標(biāo)數(shù)組的開始起始位置
int length : 要copy的數(shù)組的長度
示例:size為6,我們調(diào)用add(2,element)方法,則會(huì)從index=2+1=3的位置開始,將數(shù)組元素替換為從index起始位置為index=2,長度為6-2=4的數(shù)據(jù)。


image.png

3. addAll(Collection<? extends E> c),將指定集合追加到列表的末尾,返回值為true或者false,還可能拋出異常

// 在列表的結(jié)尾追加一個(gè)集合
public boolean addAll(Collection<? extends E> c) {
    Object[] a = c.toArray();
    int numNew = a.length;
    // 擴(kuò)容
    ensureCapacityInternal(size + numNew);  // Increments modCount
    // 將原數(shù)組與指定數(shù)組拼接到一起
    System.arraycopy(a, 0, elementData, size, numNew);
    size += numNew;
    return numNew != 0;
}

4. addAll(int index, Collection<? extends E> c),在指定位置追加指定元素列表,返回值為true或者false,還可能拋出異常

public boolean addAll(int index, Collection<? extends E> c) {
    // 檢測(cè)指定位置是否合法,是否超出邊界
    rangeCheckForAdd(index);

    Object[] a = c.toArray();
    int numNew = a.length;
    // 擴(kuò)展容量
    ensureCapacityInternal(size + numNew);  // Increments modCount

    int numMoved = size - index;
    // 如果指定位置在原列表之間,則分兩部分復(fù)制數(shù)組
    if (numMoved > 0)
        System.arraycopy(elementData, index, elementData, index + numNew,
                numMoved);
    // 如果指定位置正好在列表的位置,直接在列表尾部添加指定集合即可
    System.arraycopy(a, 0, elementData, index, numNew);
    size += numNew;
    return numNew != 0;
}

5. remove(int index),移除指定下標(biāo)對(duì)應(yīng)的列表元素,返回值為指定下標(biāo)對(duì)應(yīng)的列表元素

/**
     *按下標(biāo)移除元素
     */
    public E remove(int index) {
        //先判斷index是否大于等于size,如果大于等于將報(bào)異常
        rangeCheck(index);
 
        modCount++;
        //防止數(shù)組index下標(biāo)位置所指向的內(nèi)存在移動(dòng)元素的時(shí)候被占用
        E oldValue = elementData(index);
        //需要在elementData數(shù)組中移動(dòng)元素的長度
        int numMoved = size - index - 1;
        if (numMoved > 0)
            //將elementData從下標(biāo)index+1開始的元素到,長度為numMoved,移除到elementData的下標(biāo)為index開始的地方
            //這樣就能將elementData中下標(biāo)為index的元素覆蓋掉
            System.arraycopy(elementData, index+1, elementData, index,numMoved);
        elementData[--size] = null; // Let gc do its work
 
        return oldValue;
    }

6. remove(Object o),移除列表中的指定元素,返回值為true或者false,也可拋出異常

public boolean remove(Object o) {
    if (o == null) {
        // 如果移除的指定元素為null,先找到列表中值為null的元素的下標(biāo),然后移除元素
        for (int index = 0; index < size; index++)
            if (elementData[index] == null) {
                fastRemove(index);
                return true;
            }
    } else {
        // 如果移除的元素不為null,先找到下標(biāo),然后移除元素
        for (int index = 0; index < size; index++)
            if (o.equals(elementData[index])) {
                fastRemove(index);
                return true;
            }
    }
    return false;
}

// 用指定下標(biāo)將列表分割為兩個(gè)區(qū)域,然后撮合兩個(gè)列表成為一個(gè)新的列表,達(dá)到移除指定元素作用
private void fastRemove(int index) {
    modCount++;
    int numMoved = size - index - 1;
    if (numMoved > 0)
        System.arraycopy(elementData, index+1, elementData, index,
                         numMoved);
    // 通知垃圾回收                    
    elementData[--size] = null; 
}

7. removeAll(Collection<?> c),移除指定的集合元素

public boolean removeAll(Collection<?> c) {
    // 采用Objects工具類,如果指定集合為null,則拋出空指針異常
    Objects.requireNonNull(c);
    return batchRemove(c, false);
}

private boolean batchRemove(Collection<?> c, boolean complement) {
    final Object[] elementData = this.elementData;
    int r = 0, w = 0;
    boolean modified = false;
    try {
        for (; r < size; r++)
            if (c.contains(elementData[r]) == complement)
                elementData[w++] = elementData[r];
    } finally {
        // Preserve behavioral compatibility with AbstractCollection,
        // even if c.contains() throws.
        if (r != size) {
            System.arraycopy(elementData, r,
                             elementData, w,
                             size - r);
            w += size - r;
        }
        if (w != size) {
            // clear to let GC do its work
            for (int i = w; i < size; i++)
                elementData[i] = null;
            modCount += size - w;
            size = w;
            modified = true;
        }
    }
    return modified;
}

8. removeIf(Predicate<? super E> filter),根據(jù)指定的篩選條件刪除指定的列表元素,這是Jdk1.8新增的方法

@Override
public boolean removeIf(Predicate<? super E> filter) {
    // Objects工具類判定null操作
    Objects.requireNonNull(filter);
    
    int removeCount = 0;
    // 裝載int類型的小型數(shù)組,可以理解為裝載int的列表
    final BitSet removeSet = new BitSet(size);
    // 因?yàn)楹竺娴谋闅v列表元素,可能有多線程同時(shí)訪問的情況,固做此標(biāo)記
    final int expectedModCount = modCount;
    final int size = this.size;
    // 遍歷列表元素找到符合篩選條件的下標(biāo),然后存儲(chǔ)到BitSet中
    for (int i = 0; modCount == expectedModCount && i < size; i++) {
        @SuppressWarnings("unchecked") final E element = (E) elementData[i];
        if (filter.test(element)) {
            removeSet.set(i);
            removeCount++;
        }
    }
    // 上面如果有多線程同時(shí)訪問,則此處可能會(huì)報(bào)異常
    if (modCount != expectedModCount) {
        throw new ConcurrentModificationException();
    }

    // shift surviving elements left over the spaces left by removed elements
    final boolean anyToRemove = removeCount > 0;
    // 如果符合篩選提交的元素個(gè)數(shù)不為0,則進(jìn)行刪除操作
    if (anyToRemove) {
        // 移除符合篩選條件的元素后,列表的長度
        final int newSize = size - removeCount;
        
        for (int i = 0, j = 0; (i < size) && (j < newSize); i++, j++) {
            // 可以參加下面的testBitSet測(cè)試分析,它表示返回的是正序排列BitSet中沒有的int類型值
            i = removeSet.nextClearBit(i);
            elementData[j] = elementData[i];
        }
        for (int k = newSize; k < size; k++) {
            elementData[k] = null;  // Let gc do its work
        }
        this.size = newSize;
        if (modCount != expectedModCount) {
            throw new ConcurrentModificationException();
        }
        modCount++;
    }
    return anyToRemove;
}

測(cè)試BitSet類型的存儲(chǔ),主要測(cè)試nextClearBit方法


微信圖片_20190821185923.jpg
public void testBitSet() {
    // 長度為6,對(duì)應(yīng)存儲(chǔ)元素為{0,1,2,3,4,5}
    BitSet removeSet = new BitSet(6);
    // 如下為裝載的數(shù)據(jù),淺綠色標(biāo)記
    removeSet.set(1);
    removeSet.set(0);
    removeSet.set(3);
    removeSet.set(5);

    // 可采用如下方式,找到為裝載的數(shù)據(jù),即為{2,4}
    for (int i = 0; i < 6; i++) {
        System.out.println("清除前i=" + i);
        i = removeSet.nextClearBit(i);
        System.out.println("清除后i=" + i);
        System.out.println("-----------------");
    }
}

清除前i=0
清除后i=2
-----------------
清除前i=3
清除后i=4
-----------------
清除前i=5
清除后i=6
-----------------

測(cè)試removeIf(Predicate<? super E> filter)方法

public void testRemoveIf() {
    ArrayList<String> mArrayList = new ArrayList<>();
    mArrayList.add("a");
    mArrayList.add("b");
    mArrayList.add("ab");
    mArrayList.add("c");
    mArrayList.add("aa");
    mArrayList.add("d");
    System.out.println("初始時(shí):" + mArrayList.toString());
    mArrayList.removeIf(s -> s.contains("a"));
    System.out.println("過濾完:" + mArrayList.toString());

    // 初始時(shí):[a, b, ab, c, aa, d]
    // 過濾完:[b, c, d]
}

9. clone(),克隆列表,是一種淺拷貝,拷貝的裝載對(duì)象的內(nèi)存地址

public Object clone() {
    try {
        ArrayList<?> v = (ArrayList<?>) super.clone();
        v.elementData = Arrays.copyOf(elementData, size);
        v.modCount = 0;
        return v;
    } catch (CloneNotSupportedException e) {
        // this shouldn't happen, since we are Cloneable
        throw new InternalError(e);
    }
}

10. set(int index, E element),用指定元素替換指定下標(biāo)的值并返回

public E set(int index, E element) {
    rangeCheck(index);

    E oldValue = elementData(index);
    elementData[index] = element;
    return oldValue;
}

11.get(int index)方法:返回指定下標(biāo)處的元素的值。

public E get(int index) {
        rangeCheck(index);
        return elementData(index);
    }

rangeCheck(index)會(huì)檢測(cè)index值是否合法,如果合法則返回索引對(duì)應(yīng)的值。

5、總結(jié)

1:ArrayList 本質(zhì)實(shí)現(xiàn)方法是用數(shù)組!是非同步的!

2:初始化容量 = 10 ,最大容量不會(huì)超過 MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8!

3:indexOf和lastIndexOf 查找元素,若元素不存在,則返回-1!

4:當(dāng)ArrayList容量不足以容納全部元素時(shí),ArrayList會(huì)重新設(shè)置容量:新的容量=“(原始容量x3)/2 + 1”。

5:ArrayList的克隆函數(shù),即是將全部元素克隆到一個(gè)數(shù)組中。

6:ArrayList實(shí)現(xiàn)java.io.Serializable的方式。當(dāng)寫入到輸出流時(shí),先寫入“容量”,再依次寫入“每一個(gè)元素”;當(dāng)讀出輸入流時(shí),先讀取“容量”,再依次讀取“每一個(gè)元素”。

7:造成ArrayList添加(add)慢的原因是什么?
當(dāng)容量不夠時(shí),每次增加元素,使用System.arraycopy()方法將原來的元素拷貝到一個(gè)新的數(shù)組中,造成了資源的浪費(fèi),也因此建議在事先能確定元素?cái)?shù)量的情況下,在使用ArrayList。

8:ArrayList的實(shí)現(xiàn)中大量地調(diào)用了Arrays.copyof()和System.arraycopy()方法。

9:造成ArrayList移除(remove)慢的原因是什么?
ArrayList基于數(shù)組實(shí)現(xiàn),可以通過下標(biāo)索引直接查找到指定位置的元素,因此查找效率高,但每次根據(jù)下標(biāo)插入或刪除元素時(shí),就需要調(diào)用System.arraycopy()方法將下標(biāo)前數(shù)據(jù)和下標(biāo)后的數(shù)據(jù)進(jìn)行拼接并復(fù)制到新的數(shù)組里,因此插入、刪除元素的效率低。

10:在查找給定元素索引值等的方法中,源碼都將該元素的值分為null和不為null兩種情況處理,ArrayList中允許元素為null。

11:ArrayList為什么能自動(dòng)擴(kuò)展大小,不需要手動(dòng)設(shè)置大???
ArrayList內(nèi)部采用的是Array對(duì)象存儲(chǔ),本身該對(duì)象是需要定義長度的,所以每次添加數(shù)據(jù)的時(shí)候,都是判斷是否先擴(kuò)展容量,且容量有最大值限制,容量的大小是在原來基礎(chǔ)上擴(kuò)大1.5倍:公式(oldCapacity + oldCapacity >> 1)。

12:ArrayLIst查詢效率高:ArrayLIst是連續(xù)存放元素的,找到第一個(gè)元素的首地址,再加上每個(gè)元素的占據(jù)的字節(jié)大小就能定位到對(duì)應(yīng)的元素。

13:LinkedList插入刪除效率高。因?yàn)閳?zhí)行插入刪除操作時(shí),只需要操作引用即可,元素不需要移動(dòng)元素,他們分布在內(nèi)存的不同地方,通過引用來互相關(guān)聯(lián)起來。而ArrayLIst需要移動(dòng)元素,故效率低。

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

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

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