成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專(zhuān)欄INFORMATION COLUMN

深入了解Java集合中的ArrayList

zeyu / 2173人閱讀

摘要:實(shí)現(xiàn)了接口,所以包含的基本方法新增,刪除,插入等都實(shí)現(xiàn)了。線程安全問(wèn)題在多線程情況下有線程進(jìn)行修改時(shí),是線程不安全的。線程安全性問(wèn)題,取決于如何應(yīng)用。反映的是當(dāng)前數(shù)組中存了多少數(shù)據(jù),而則反映的是內(nèi)部數(shù)組的容量。

什么是ArrayList

ArrayList 是一個(gè)可擴(kuò)容數(shù)組Resizable-array,它實(shí)現(xiàn)了List接口的所有方法。

從對(duì)ArrayList的簡(jiǎn)單描述中我們可以得出幾點(diǎn)

ArrayList 是數(shù)組,但不同于一般數(shù)組,可擴(kuò)容,而一般數(shù)組容量固定。

ArrayList 實(shí)現(xiàn)了List接口,所以List包含的基本方法(新增,刪除,插入等)ArrayList都實(shí)現(xiàn)了。

ArrayList 需要知道的10點(diǎn)內(nèi)容 1.ArrayList 內(nèi)部是通過(guò)數(shù)組實(shí)現(xiàn)
ArrayList 內(nèi)部實(shí)現(xiàn)是通過(guò)一個(gè)對(duì)象數(shù)組
transient Object[] elementData
2.ArrayList 添加操作(add) 的執(zhí)行時(shí)間是固定的
ArrayList add() 方法實(shí)質(zhì)上是實(shí)現(xiàn)了在數(shù)組的末尾添加一個(gè)元素 elementData[size++] = el 所以執(zhí)行的時(shí)間復(fù)雜度是固定的O(1),添加n個(gè)元素為O(n)
3.除了 add 方法外其他操作如: 插入、刪除 操作時(shí)間是線性的。
因?yàn)楸举|(zhì)上是對(duì)數(shù)組進(jìn)行操作,當(dāng)在數(shù)組中插入、刪除數(shù)據(jù)時(shí),需要先對(duì)數(shù)組進(jìn)行移位操作,插入時(shí)需要將插入點(diǎn)以后的數(shù)據(jù)全部后移,而刪除操作則需要將刪除節(jié)點(diǎn)后的數(shù)據(jù)全部前移,操作時(shí)間復(fù)雜度為O(n)。
4.線程安全問(wèn)題
ArrayList 在多線程情況下有線程進(jìn)行修改時(shí),是線程不安全的。線程安全性問(wèn)題,取決于如何應(yīng)用。List list = Collections.synchronizedList(new ArrayList(...))可以獲取線程安全的ArrayList
5.關(guān)于擴(kuò)容

ArrayList 有其自動(dòng)擴(kuò)容機(jī)制也可以在預(yù)知要處理數(shù)據(jù)大小時(shí)手動(dòng)擴(kuò)容

1.自動(dòng)擴(kuò)容

ArrayList 在新增元素時(shí)(add ,addAll操作)會(huì)先檢測(cè)當(dāng)前內(nèi)部數(shù)組elementData[]的容量是否足夠添加新元素,如果不足則擴(kuò)容,ArrayList最大容量為Integer.MAX_VALUE,自動(dòng)擴(kuò)容調(diào)用的流程如下

public boolean add(E e) {
    // 自動(dòng)擴(kuò)容,并記錄元素修改數(shù)量 ,元素修改數(shù)量主要是用于并發(fā)修改錯(cuò)誤
    ensureCapacityInternal(size + 1); 
    elementData[size++] = e;
    return true;
}

private void ensureCapacityInternal(int minCapacity) {
    ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}

// 返回一個(gè)數(shù)組大小的值 minCapacity 和默認(rèn)的 DEFAULT_CAPACITY 中較大的那個(gè)
private static int calculateCapacity(Object[] elementData, int minCapacity) {
    // 如果是空數(shù)組 通過(guò)new ArrayList()創(chuàng)建
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
      // 默認(rèn)大小是DEFAULT_CAPACITY =  10
      return Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    return minCapacity;
}


實(shí)際擴(kuò)充容量的片段

private void ensureExplicitCapacity(int minCapacity) {
    modCount++;
    // 將上面方法返回的大小和當(dāng)前 elementData 大小進(jìn)行比較,判斷是否需要擴(kuò)容
    if (minCapacity - elementData.length > 0)
      grow(minCapacity);
}
private void grow(int minCapacity) { // 具體擴(kuò)容代碼
    int oldCapacity = elementData.length;
    // 擴(kuò)容后容量總是擴(kuò)容前的大約1.5倍左右增量0.5
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    if (newCapacity - minCapacity < 0)
          newCapacity = minCapacity;
    // 如果擴(kuò)容后超出了最大個(gè)數(shù)限制 則由hugeCapacity()來(lái)處理
    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);
}
private static int hugeCapacity(int minCapacity) {
    if (minCapacity < 0) // overflow
          throw new OutOfMemoryError();
    return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE;
}

2.手動(dòng)擴(kuò)容或者指定初始容量

ArrayList 手動(dòng)擴(kuò)容主要是調(diào)用ensureCapacity(int minCapacity)方法,除此之外還能在創(chuàng)建ArrayList時(shí)指定初始容量

創(chuàng)建時(shí)直接指定容量new ArrayList<>(initialCapacity)及其實(shí)現(xiàn)

public ArrayList(int initialCapacity) {
    if (initialCapacity > 0) {
        // 創(chuàng)建指定容量的一個(gè)數(shù)組
        this.elementData = new Object[initialCapacity];
    } else if (initialCapacity == 0) {
        this.elementData = EMPTY_ELEMENTDATA;
    } else {
        throw new IllegalArgumentException("Illegal Capacity: "+
                                           initialCapacity);
    }
}

通過(guò)ensureCapacity(int minCapacity)方法在處理過(guò)程中擴(kuò)容,這種情況一般發(fā)生在處理過(guò)程中發(fā)現(xiàn)初始容量不能滿(mǎn)足當(dāng)前數(shù)據(jù)需求,而又為了避免自動(dòng)擴(kuò)容時(shí)的資源浪費(fèi),因?yàn)槊看巫詣?dòng)擴(kuò)容時(shí)都會(huì)進(jìn)行數(shù)組復(fù)制。

public void ensureCapacity(int minCapacity) {
    int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) ? 0: DEFAULT_CAPACITY;
    if (minCapacity > minExpand) {
        ensureExplicitCapacity(minCapacity);
    }
}

3.最后看看擴(kuò)容時(shí)的數(shù)組復(fù)制,數(shù)組復(fù)制是通過(guò)Arrays.copyOf(origin,newLenght)方法來(lái)實(shí)現(xiàn)的

public static  T[] copyOf(T[] original, int newLength) {
    return (T[]) copyOf(original, newLength, original.getClass());
}
// 具體復(fù)制代碼
public static  T[] copyOf(U[] original, int newLength, Class newType) {
    @SuppressWarnings("unchecked")
    T[] copy = ((Object)newType == (Object)Object[].class)
        ? (T[]) new Object[newLength]
        : (T[]) Array.newInstance(newType.getComponentType(), newLength);
    // 將舊數(shù)組中的數(shù)據(jù)復(fù)制到新數(shù)組
    System.arraycopy(original, 0, copy, 0,
                     Math.min(original.length, newLength));
    return copy;
}
6.關(guān)于ArrayList在方法中傳遞的問(wèn)題。
ArrayList在方法中傳遞時(shí),傳遞的是對(duì)象的引用,當(dāng)某一個(gè)方法修改了ArrayList時(shí),該修改會(huì)反映到引用該ArrayList的所有地方,不管傳遞多少次,都引用的時(shí)同一個(gè)內(nèi)存區(qū)域的數(shù)據(jù),所以如果要實(shí)現(xiàn)在傳遞時(shí)確保內(nèi)容不變,應(yīng)該克隆(用ArrayList的clone())方法一份進(jìn)行傳遞(需要注意深淺克隆問(wèn)題深克隆可以使用Collections.copy(dest,src)方法),具體該怎么弄,還是要視需求而定。
7.ArrayList 的capacity和size的關(guān)系
如果要找他們之間的關(guān)系可能就是size=元素?cái)?shù)量 <= capacity = 容量大小吧。size 反映的是當(dāng)前數(shù)組中存了多少數(shù)據(jù),而capacity則反映的是ArrayList內(nèi)部數(shù)組的容量。不能習(xí)慣性認(rèn)為capacity 既是 size
8.ArrayList 和 LinkedList的選擇性問(wèn)題
ArrayList和LinkedList為什么會(huì)存在選擇性問(wèn)題,因?yàn)樗麄冊(cè)?b>get、add、remove時(shí)性能是不一樣的。

ArrayList是內(nèi)部實(shí)現(xiàn)是數(shù)組,在隨機(jī)存取時(shí)時(shí)間復(fù)雜度是O(1)知道索引就能立馬取值,而其插入和刪除操作就相對(duì)比較麻煩,需要進(jìn)行移位操作線性的時(shí)間復(fù)雜度。

LinkedList 是雙向連表Doubly-linked ,在插入和刪除時(shí)時(shí)間復(fù)雜度都是O(1),但索引(取索引位上的值)時(shí)需要從表頭或者表尾進(jìn)行遍歷操作。

所以選用哪一個(gè),完全取決于你要進(jìn)行的操作是以隨機(jī)存取為主還是增刪元素較多為主。

9.為什么elementData使用transient修飾
transient關(guān)鍵字的作用是阻止對(duì)象序列化,那么為什么要防止elementData序列化呢?那是因?yàn)閑lementData是一個(gè)數(shù)組,并且并不是數(shù)組中每一個(gè)位置上都保存有值,容量10000的數(shù)組中可能只保存了10個(gè)對(duì)象。所以不能對(duì)其進(jìn)行序列化,在ArrayList中重寫(xiě)了writeObject 、readObject 方法來(lái)對(duì)ArrayList進(jìn)行序列化控制。
10.ArrayList實(shí)現(xiàn)RandomAccess接口有什么用
RandomAccess接口是一個(gè)標(biāo)記接口并沒(méi)有定義任何方法,ArrayList 實(shí)現(xiàn)它是標(biāo)記ArrayList支持快速隨機(jī)訪問(wèn)這一特性!
11.并發(fā)修改異常ConcurrentModificationException

并發(fā)修改異常ConcurrentModificationException通常出現(xiàn)在對(duì)一個(gè)List進(jìn)行遍歷時(shí),對(duì)正在遍歷的數(shù)組進(jìn)行了修改性操作(修改性操作:改變大?。╯ize=數(shù)量)的操作,而不是指具體值)(add,remove,clear等)時(shí)便會(huì)拋出這個(gè)異常,在ArrayList內(nèi)部有一個(gè)protected transient int modCount = 0的變量用于記錄對(duì)ArrayList的修改,比如add方法代碼

public boolean add(E e) {
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    elementData[size++] = e;
    return true;
}
private void ensureCapacityInternal(int minCapacity) {
    ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private void ensureExplicitCapacity(int minCapacity) {
    modCount++;
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}

可以看到在調(diào)用add方法時(shí)會(huì)執(zhí)行modCount++操作以此標(biāo)記最新修改的數(shù)量modCount

而在遍歷時(shí)比如forEach先看代碼

public void forEach(Consumer action) {
    Objects.requireNonNull(action);
    final int expectedModCount = modCount;
    @SuppressWarnings("unchecked")
    final E[] elementData = (E[]) this.elementData;
    final int size = this.size;
    for (int i=0; modCount == expectedModCount && i < size; i++) {
        action.accept(elementData[i]);
    }
    if (modCount != expectedModCount) {
        throw new ConcurrentModificationException();
    }
}

可以看到for循環(huán)中一個(gè)明確的條件是modCount == expectedModCount 每次遍歷都會(huì)檢測(cè)該條件是否成立,而在進(jìn)入該段代碼之前先用final int expectedModCount = modCount;來(lái)保存代碼執(zhí)行之前的修改數(shù)量,當(dāng)進(jìn)入遍歷后,有了更改操作,就會(huì)使得expectedModCount 和modCount不相等此時(shí)便會(huì)拋出ConcurrentModificationException異常,對(duì)于其他的修改操作,原理都是類(lèi)似的。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/74311.html

相關(guān)文章

  • java源碼

    摘要:集合源碼解析回歸基礎(chǔ),集合源碼解析系列,持續(xù)更新和源碼分析與是兩個(gè)常用的操作字符串的類(lèi)。這里我們從源碼看下不同狀態(tài)都是怎么處理的。 Java 集合深入理解:ArrayList 回歸基礎(chǔ),Java 集合深入理解系列,持續(xù)更新~ JVM 源碼分析之 System.currentTimeMillis 及 nanoTime 原理詳解 JVM 源碼分析之 System.currentTimeMi...

    Freeman 評(píng)論0 收藏0
  • ArrayList源碼解析之fail-fast機(jī)制深入理解

    摘要:當(dāng)多個(gè)線程對(duì)同一個(gè)集合的內(nèi)容進(jìn)行操作時(shí),就可能會(huì)產(chǎn)生事件。當(dāng)某一個(gè)線程遍歷的過(guò)程中,的內(nèi)容被另外一個(gè)線程所改變了就會(huì)拋出異常,產(chǎn)生事件。在線程在遍歷過(guò)程中的某一時(shí)刻,線程執(zhí)行了,并且線程刪除了中的節(jié)點(diǎn)。 概要 前面,我們已經(jīng)學(xué)習(xí)了ArrayList。接下來(lái),我們以ArrayList為例,對(duì)Iterator的fail-fast機(jī)制進(jìn)行了解。 1 fail-fast簡(jiǎn)介 fail-fast...

    NikoManiac 評(píng)論0 收藏0
  • Java深入 - 深入理解Java集合

    摘要:集合集合類(lèi)存放于包中。迭代器,可以通過(guò)迭代器遍歷集合中的數(shù)據(jù)是映射表的基礎(chǔ)接口有序集合的是非常常用的數(shù)據(jù)類(lèi)型。按照指定的迭代器所返回的元素順序,將該中的所有元素添加到此列表的尾部。 集合集合類(lèi)存放于Java.util包中。集合類(lèi)型主要有3種:set(集)、list(列表包含Queue)和map(映射)。 Collection:Collection是集合的基本接口,List、Set、Qu...

    hlcfan 評(píng)論0 收藏0
  • Java相關(guān)

    摘要:本文是作者自己對(duì)中線程的狀態(tài)線程間協(xié)作相關(guān)使用的理解與總結(jié),不對(duì)之處,望指出,共勉。當(dāng)中的的數(shù)目而不是已占用的位置數(shù)大于集合番一文通版集合番一文通版垃圾回收機(jī)制講得很透徹,深入淺出。 一小時(shí)搞明白自定義注解 Annotation(注解)就是 Java 提供了一種元程序中的元素關(guān)聯(lián)任何信息和著任何元數(shù)據(jù)(metadata)的途徑和方法。Annotion(注解) 是一個(gè)接口,程序可以通過(guò)...

    wangtdgoodluck 評(píng)論0 收藏0
  • 記一次慘痛的面試經(jīng)歷

    摘要:把內(nèi)存分成兩種,一種叫做棧內(nèi)存,一種叫做堆內(nèi)存在函數(shù)中定義的一些基本類(lèi)型的變量和對(duì)象的引用變量都是在函數(shù)的棧內(nèi)存中分配。堆內(nèi)存用于存放由創(chuàng)建的對(duì)象和數(shù)組。 一次慘痛的阿里技術(shù)面 就在昨天,有幸接到了阿里的面試通知,本來(lái)我以為自己的簡(jiǎn)歷應(yīng)該不會(huì)的到面試的機(jī)會(huì)了,然而機(jī)會(huì)卻這么來(lái)了,我卻沒(méi)有做好準(zhǔn)備,被面試官大大一通血虐。因此,我想寫(xiě)點(diǎn)東西紀(jì)念一下這次的經(jīng)歷,也當(dāng)一次教訓(xùn)了。其實(shí)面試官大大...

    CoorChice 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<