国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

Java 常用List集合使用場(chǎng)景分析

godruoyi / 2189人閱讀

摘要:常用集合使用場(chǎng)景分析過年前的最后一篇,本章通過介紹,,,底層實(shí)現(xiàn)原理和四個(gè)集合的區(qū)別。和都是線程安全的,不同的是前者使用類,后者使用關(guān)鍵字。面試官會(huì)認(rèn)為你是一個(gè)基礎(chǔ)扎實(shí),內(nèi)功深厚的人才到這里常用集合使用場(chǎng)景分析就結(jié)束了。

Java 常用List集合使用場(chǎng)景分析

過年前的最后一篇,本章通過介紹ArrayList,LinkedList,Vector,CopyOnWriteArrayList 底層實(shí)現(xiàn)原理和四個(gè)集合的區(qū)別。讓你清楚明白,為什么工作中會(huì)常用ArrayList和CopyOnWriteArrayList?了解底層實(shí)現(xiàn)原理,我們可以學(xué)習(xí)到很多代碼設(shè)計(jì)的思路,開闊自己的思維。本章通俗易懂,還在等什么,快來學(xué)習(xí)吧!

知識(shí)圖解:

技術(shù):ArrayList,LinkedList,Vector,CopyOnWriteArrayList
說明:本章基于jdk1.8,github上有ArrayList,LinkedList的簡(jiǎn)單源碼代碼
源碼:https://github.com/ITDragonBl...

知識(shí)預(yù)覽

ArrayList : 基于數(shù)組實(shí)現(xiàn)的非線程安全的集合。查詢?cè)乜欤迦耄瑒h除中間元素慢。
LinkedList : 基于鏈表實(shí)現(xiàn)的非線程安全的集合。查詢?cè)芈迦耄瑒h除中間元素快。
Vector : 基于數(shù)組實(shí)現(xiàn)的線程安全的集合。線程同步(方法被synchronized修飾),性能比ArrayList差。
CopyOnWriteArrayList : 基于數(shù)組實(shí)現(xiàn)的線程安全的寫時(shí)復(fù)制集合。線程安全(ReentrantLock加鎖),性能比Vector高,適合讀多寫少的場(chǎng)景。

ArrayList 和 LinkedList 讀寫快慢的本質(zhì)

ArrayList : 查詢數(shù)據(jù)快,是因?yàn)閿?shù)組可以通過下標(biāo)直接找到元素。 寫數(shù)據(jù)慢有兩個(gè)原因:一是數(shù)組復(fù)制過程需要時(shí)間,二是擴(kuò)容需要實(shí)例化新數(shù)組也需要時(shí)間。
LinkedList : 查詢數(shù)據(jù)慢,是因?yàn)殒湵硇枰?strong>遍歷每個(gè)元素直到找到為止。 寫數(shù)據(jù)快有一個(gè)原因:除了實(shí)例化對(duì)象需要時(shí)間外,只需要修改指針即可完成添加和刪除元素。
本章會(huì)通過源碼分析,驗(yàn)證上面的說法。

注:這里的塊和慢是相對(duì)的。并不是LinkedList的插入和刪除就一定比ArrayList快。明白其快慢的本質(zhì):ArrayList快在定位,慢在數(shù)組復(fù)制。LinkedList慢在定位,快在指針修改

ArrayList

ArrayList 是基于動(dòng)態(tài)數(shù)組實(shí)現(xiàn)的非線程安全的集合。當(dāng)?shù)讓訑?shù)組滿的情況下還在繼續(xù)添加的元素時(shí),ArrayList則會(huì)執(zhí)行擴(kuò)容機(jī)制擴(kuò)大其數(shù)組長(zhǎng)度。ArrayList查詢速度非常快,使得它在實(shí)際開發(fā)中被廣泛使用。美中不足的是插入和刪除元素較慢,同時(shí)它并不是線程安全的。
我們可以從源碼中找到答案

// 查詢?cè)?public E get(int index) {
    rangeCheck(index);                    // 檢查是否越界
    return elementData(index);
}
// 順序添加元素
public boolean add(E e) {
    ensureCapacityInternal(size + 1);      // 擴(kuò)容機(jī)制
    elementData[size++] = e;
    return true;
}
// 從數(shù)組中間添加元素
public void add(int index, E element) {
    rangeCheckForAdd(index);            // 數(shù)組下標(biāo)越界檢查
    ensureCapacityInternal(size + 1);      // 擴(kuò)容機(jī)制
    System.arraycopy(elementData, index, elementData, index + 1, size - index); // 復(fù)制數(shù)組
    elementData[index] = element;        // 替換元素
    size++;
}
// 從數(shù)組中刪除元素
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; // clear to let GC do its work
}

從源碼中可以得知,
ArrayList在執(zhí)行查詢操作時(shí):
第一步:先判斷下標(biāo)是否越界。
第二步:然后在直接通過下標(biāo)從數(shù)組中返回元素。

ArrayList在執(zhí)行順序添加操作時(shí):
第一步:通過擴(kuò)容機(jī)制判斷原數(shù)組是否還有空間,若沒有則重新實(shí)例化一個(gè)空間更大的新數(shù)組,把舊數(shù)組的數(shù)據(jù)拷貝到新數(shù)組中。
第二步:在新數(shù)組的最后一位元素添加值。

ArrayList在執(zhí)行中間插入操作時(shí):
第一步:先判斷下標(biāo)是否越界。
第二步:擴(kuò)容。
第三步:若插入的下標(biāo)為i,則通過復(fù)制數(shù)組的方式將i后面的所有元素,往后移一位。
第四步:新數(shù)據(jù)替換下標(biāo)為i的舊元素。
刪除也是一樣:只是數(shù)組往前移了一位,最后一個(gè)元素設(shè)置為null,等待JVM垃圾回收。

從上面的源碼分析,我們可以得到一個(gè)結(jié)論和一個(gè)疑問。
結(jié)論是:ArrayList快在下標(biāo)定位,慢在數(shù)組復(fù)制。
疑問是:能否將每次擴(kuò)容的長(zhǎng)度設(shè)置大點(diǎn),減少擴(kuò)容的次數(shù),從而提高效率?其實(shí)每次擴(kuò)容的長(zhǎng)度大小是很有講究的。若擴(kuò)容的長(zhǎng)度太大,會(huì)造成大量的閑置空間;若擴(kuò)容的長(zhǎng)度太小,會(huì)造成頻發(fā)的擴(kuò)容(數(shù)組復(fù)制),效率更低。

LinkedList

LinkedList 是基于雙向鏈表實(shí)現(xiàn)的非線程安全的集合,它是一個(gè)鏈表結(jié)構(gòu),不能像數(shù)組一樣隨機(jī)訪問,必須是每個(gè)元素依次遍歷直到找到元素為止。其結(jié)構(gòu)的特殊性導(dǎo)致它查詢數(shù)據(jù)慢。
我們可以從源碼中找到答案

// 查詢?cè)?public E get(int index) {
    checkElementIndex(index);     // 檢查是否越界
    return node(index).item;
}
Node node(int index) {
    if (index < (size >> 1)) {     // 類似二分法
        Node x = first;
        for (int i = 0; i < index; i++)
            x = x.next;
        return x;
    } else {
        Node x = last;
        for (int i = size - 1; i > index; i--)
            x = x.prev;
        return x;
    }
}
// 插入元素
public void add(int index, E element) {
    checkPositionIndex(index);    // 檢查是否越界
    if (index == size)            // 在鏈表末尾添加
        linkLast(element);
    else                        // 在鏈表中間添加
        linkBefore(element, node(index));
}
void linkBefore(E e, Node succ) {
    final Node pred = succ.prev; 
    final Node newNode = new Node<>(pred, e, succ);
    succ.prev = newNode;
    if (pred == null)    
        first = newNode;
    else                
        pred.next = newNode;
    size++;
    modCount++;
}

從源碼中可以得知,
LinkedList在執(zhí)行查詢操作時(shí):
第一步:先判斷元素是靠近頭部,還是靠近尾部。
第二步:若靠近頭部,則從頭部開始依次查詢判斷。和ArrayList的elementData(index)相比當(dāng)然是慢了很多。

LinkedList在插入元素的思路:
第一步:判斷插入元素的位置是鏈表的尾部,還是中間。
第二步:若在鏈表尾部添加元素,直接將尾節(jié)點(diǎn)的下一個(gè)指針指向新增節(jié)點(diǎn)。
第三步:若在鏈表中間添加元素,先判斷插入的位置是否為首節(jié)點(diǎn),是則將首節(jié)點(diǎn)的上一個(gè)指針指向新增節(jié)點(diǎn)。否則先獲取當(dāng)前節(jié)點(diǎn)的上一個(gè)節(jié)點(diǎn)(簡(jiǎn)稱A),并將A節(jié)點(diǎn)的下一個(gè)指針指向新增節(jié)點(diǎn),然后新增節(jié)點(diǎn)的下一個(gè)指針指向當(dāng)前節(jié)點(diǎn)。

Vector

Vector 的數(shù)據(jù)結(jié)構(gòu)和使用方法與ArrayList差不多。最大的不同就是Vector是線程安全的。從下面的源碼可以看出,幾乎所有的對(duì)數(shù)據(jù)操作的方法都被synchronized關(guān)鍵字修飾。synchronized是線程同步的,當(dāng)一個(gè)線程已經(jīng)獲得Vector對(duì)象的鎖時(shí),其他線程必須等待直到該鎖被釋放。從這里就可以得知Vector的性能要比ArrayList低。
若想要一個(gè)高性能,又是線程安全的ArrayList,可以使用Collections.synchronizedList(list);方法或者使用CopyOnWriteArrayList集合

public synchronized E get(int index) {
    if (index >= elementCount)
        throw new ArrayIndexOutOfBoundsException(index);

    return elementData(index);
}
public synchronized boolean add(E e) {
    modCount++;
    ensureCapacityHelper(elementCount + 1);
    elementData[elementCount++] = e;
    return true;
}
public synchronized boolean removeElement(Object obj) {
    modCount++;
    int i = indexOf(obj);
    if (i >= 0) {
        removeElementAt(i);
        return true;
    }
    return false;
}
CopyOnWriteArrayList

在這里我們先簡(jiǎn)單了解一下CopyOnWrite容器。它是一個(gè)寫時(shí)復(fù)制的容器。當(dāng)我們往一個(gè)容器添加元素的時(shí)候,不是直接往當(dāng)前容器添加,而是先將當(dāng)前容器進(jìn)行copy一份,復(fù)制出一個(gè)新的容器,然后對(duì)新容器里面操作元素,最后將原容器的引用指向新的容器。所以CopyOnWrite容器是一種讀寫分離的思想,讀和寫不同的容器。
應(yīng)用場(chǎng)景:適合高并發(fā)的讀操作(讀多寫少)。若寫的操作非常多,會(huì)頻繁復(fù)制容器,從而影響性能。

CopyOnWriteArrayList 寫時(shí)復(fù)制的集合,在執(zhí)行寫操作(如:add,set,remove等)時(shí),都會(huì)將原數(shù)組拷貝一份,然后在新數(shù)組上做修改操作。最后集合的引用指向新數(shù)組。
CopyOnWriteArrayList 和Vector都是線程安全的,不同的是:前者使用ReentrantLock類,后者使用synchronized關(guān)鍵字。ReentrantLock提供了更多的鎖投票機(jī)制,在鎖競(jìng)爭(zhēng)的情況下能表現(xiàn)更佳的性能。就是它讓JVM能更快的調(diào)度線程,才有更多的時(shí)間去執(zhí)行線程。這就是為什么CopyOnWriteArrayList的性能在大并發(fā)量的情況下優(yōu)于Vector的原因。

private E get(Object[] a, int index) {
    return (E) a[index];
}
public boolean add(E e) {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        Object[] elements = getArray();
        int len = elements.length;
        Object[] newElements = Arrays.copyOf(elements, len + 1);
        newElements[len] = e;
        setArray(newElements);
        return true;
    } finally {
        lock.unlock();
    }
}
private boolean remove(Object o, Object[] snapshot, int index) {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        Object[] current = getArray();
        int len = current.length;
        ......
        Object[] newElements = new Object[len - 1];
        System.arraycopy(current, 0, newElements, 0, index);
        System.arraycopy(current, index + 1, newElements, index, len - index - 1);
        setArray(newElements);
        return true;
    } finally {
        lock.unlock();
    }
}
總結(jié)

看到這里,如果面試官問你ArrayList和LinkedList有什么區(qū)別時(shí)
如果你回答:ArrayList查詢快,寫數(shù)據(jù)慢;LinkedList查詢慢,寫數(shù)據(jù)快。面試官只能算你勉強(qiáng)合格。
如果你回答:ArrayList查詢快是因?yàn)榈讓邮怯蓴?shù)組實(shí)現(xiàn),通過下標(biāo)定位數(shù)據(jù)快。寫數(shù)據(jù)慢是因?yàn)閺?fù)制數(shù)組耗時(shí)。LinkedList底層是雙向鏈表,查詢數(shù)據(jù)依次遍歷慢。寫數(shù)據(jù)只需修改指針引用。
如果你繼續(xù)回答:ArrayList和LinkedList都不是線程安全的,小并發(fā)量的情況下可以使用Vector,若并發(fā)量很多,且讀多寫少可以考慮使用CopyOnWriteArrayList。
因?yàn)镃opyOnWriteArrayList底層使用ReentrantLock鎖,比使用synchronized關(guān)鍵字的Vector能更好的處理鎖競(jìng)爭(zhēng)的問題。
面試官會(huì)認(rèn)為你是一個(gè)基礎(chǔ)扎實(shí),內(nèi)功深厚的人才!!!

到這里Java 常用List集合使用場(chǎng)景分析就結(jié)束了。過年前的最后一篇博客,有點(diǎn)浮躁,可能身在職場(chǎng),心在老家!最后祝大家新年快樂!!!狗年吉祥!!!大富大貴!!!可能都沒人看博客了 ⊙﹏⊙‖∣ 哈哈哈哈(?ω?)hiahiahia

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

轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/70966.html

相關(guān)文章

  • Java面試題

    摘要:近段時(shí)間在準(zhǔn)備實(shí)習(xí)的面試,在網(wǎng)上看到一份面試題,就慢慢試著做,爭(zhēng)取每天積累一點(diǎn)點(diǎn)。現(xiàn)在每天給自己在面試題編寫的任務(wù)是題,有時(shí)候忙起來可能就沒有時(shí)間寫了,但是爭(zhēng)取日更,即使當(dāng)天沒更也會(huì)在之后的更新補(bǔ)上。 ????近段時(shí)間在準(zhǔn)備實(shí)習(xí)的面試,在網(wǎng)上看到一份面試題,就慢慢試著做,爭(zhēng)取每天積累一點(diǎn)點(diǎn)。????暫時(shí)手頭上的面試題只有一份,題量還是挺大的,有208題,所以可能講的不是很詳細(xì),只是我自...

    OnlyMyRailgun 評(píng)論0 收藏0
  • 金三銀四,2019大廠Android高級(jí)工程師面試題整理

    摘要:原文地址游客前言金三銀四,很多同學(xué)心里大概都準(zhǔn)備著年后找工作或者跳槽。最近有很多同學(xué)都在交流群里求大廠面試題。 最近整理了一波面試題,包括安卓JAVA方面的,目前大廠還是以安卓源碼,算法,以及數(shù)據(jù)結(jié)構(gòu)為主,有一些中小型公司也會(huì)問到混合開發(fā)的知識(shí),至于我為什么傾向于混合開發(fā),我的一句話就是走上編程之路,將來你要學(xué)不僅僅是這些,豐富自己方能與世接軌,做好全棧的裝備。 原文地址:游客kutd...

    tracymac7 評(píng)論0 收藏0
  • Java學(xué)習(xí)路線總結(jié),搬磚工逆襲Java架構(gòu)師(全網(wǎng)最強(qiáng))

    摘要:哪吒社區(qū)技能樹打卡打卡貼函數(shù)式接口簡(jiǎn)介領(lǐng)域優(yōu)質(zhì)創(chuàng)作者哪吒公眾號(hào)作者架構(gòu)師奮斗者掃描主頁左側(cè)二維碼,加入群聊,一起學(xué)習(xí)一起進(jìn)步歡迎點(diǎn)贊收藏留言前情提要無意間聽到領(lǐng)導(dǎo)們的談話,現(xiàn)在公司的現(xiàn)狀是碼農(nóng)太多,但能獨(dú)立帶隊(duì)的人太少,簡(jiǎn)而言之,不缺干 ? 哪吒社區(qū)Java技能樹打卡?【打卡貼 day2...

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

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

0條評(píng)論

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