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

資訊專欄INFORMATION COLUMN

源碼|jdk源碼-ArrayList與Vector源碼閱讀

0x584a / 1228人閱讀

摘要:畢業(yè)兩個(gè)星期了,開始成為一名正式的碼農(nóng)了。將指定位置的數(shù)據(jù)移除。但是問題是,為時(shí),并不是直接一個(gè)大小為的數(shù)組,而是使用靜態(tài)變量來代替。此外,函數(shù)還做了越界檢查。返回迭代器,與之有一個(gè)搭配的輔助類。

畢業(yè)兩個(gè)星期了,開始成為一名正式的java碼農(nóng)了。
一直對(duì)偏底層比較感興趣,想著深入自己的java技能,看書、讀源碼、總結(jié)、造輪子實(shí)踐都是付諸行動(dòng)的方法。
說到看源碼,就應(yīng)該由簡(jiǎn)入難,逐漸加深,那就從jdk的源碼開始看起吧。

ArrayList和Vector是java標(biāo)準(zhǔn)庫(kù)提供的一種比較簡(jiǎn)單的數(shù)據(jù)結(jié)構(gòu),也是最常用的一種。

線性表的概念 表ADT

表這種抽象概念指的是一種存放數(shù)據(jù)的容器,其中數(shù)據(jù)A1, A2, A3, ..., Ai, ... AN有序排列。

那么在表這一抽象的數(shù)據(jù)結(jié)構(gòu)模型中:

數(shù)據(jù)是有序排列的,有前后之分。

表中每個(gè)數(shù)據(jù)都有一個(gè)索引,A1元素的索引為0, Ai元素的索引為i - 1。至于索引從0開始還是從1開始編號(hào)這個(gè)不是重點(diǎn)。

表的大小為即為數(shù)據(jù)的個(gè)數(shù),即N。

它的操作有:

獲取表的大小。

取出表中給定索引的數(shù)據(jù)。

給表中給定索引的位置放入數(shù)據(jù)。

向指定位置插入數(shù)據(jù)。

將指定位置的數(shù)據(jù)移除。

表有兩種實(shí)現(xiàn)方式,數(shù)組實(shí)現(xiàn)和鏈表實(shí)現(xiàn)。這兩種實(shí)現(xiàn)方式各有優(yōu)劣,其中這里所說的ArrayList是數(shù)組實(shí)現(xiàn)方式。

實(shí)現(xiàn)思路

線性表的實(shí)現(xiàn)中是將元素放到數(shù)組中的。如果是C的數(shù)組,那其實(shí)是一塊連續(xù)的內(nèi)存。
在java中也是java的原生數(shù)組,內(nèi)存布局中邏輯上是連續(xù)的,物理上不一定。印象中有的jvm實(shí)現(xiàn)為了解決內(nèi)存碎片搞類似邏輯內(nèi)存的這種操作。

往線性表取出數(shù)據(jù)或拿出元素都能很直接對(duì)應(yīng)到原生數(shù)組中去。
但是,線性表插入數(shù)據(jù)的這一操作要求表是能夠動(dòng)態(tài)增長(zhǎng)的,但是原生數(shù)組的大小是固定的。

線性表實(shí)現(xiàn)的一大重點(diǎn)是實(shí)現(xiàn)表動(dòng)態(tài)增長(zhǎng)這一要求,將其轉(zhuǎn)換、化歸到固定大小的原生數(shù)據(jù)上去。思路是,當(dāng)線性表內(nèi)部保存數(shù)據(jù)的數(shù)組如果不夠用了,就申請(qǐng)一個(gè)更大的數(shù)組,將原來數(shù)組的數(shù)據(jù)拷貝進(jìn)去。

時(shí)間空間復(fù)雜度

由于線性表用數(shù)組實(shí)現(xiàn),因此定位元素實(shí)際上只需要計(jì)算內(nèi)存偏移量即可訪問得到,取出和放入元素的時(shí)間復(fù)雜度為O(1)。

由于數(shù)組中的元素是緊密排列的,因此在中間插入一個(gè)元素或移除一個(gè)元素需要移動(dòng)后面一排數(shù)據(jù),所以往指定位置插入或移除數(shù)據(jù)的平均時(shí)間復(fù)雜度為O(n)。

如果是往表末尾插入元素,情況比較復(fù)雜因?yàn)檫@可能觸發(fā)擴(kuò)容操作。不觸發(fā)擴(kuò)容就是O(1),如果觸發(fā)了擴(kuò)容,假設(shè)是2倍擴(kuò)容,我們可以把擴(kuò)容的時(shí)間均攤到前面每一個(gè)元素的插入操作上,平均來說,也是O(1)。

ArrayList的繼承結(jié)構(gòu)


如上圖所示,在設(shè)計(jì)上,它的結(jié)構(gòu)應(yīng)該理解成 ArrayList --> List --> Collection --> Iterable

List接口表示抽象的表,也就是上面所說的表。

Collection接口表示抽象的容器,能放元素的都算。

Iterable接口表示可迭代的對(duì)象。也即該容器內(nèi)的元素都能夠通過迭代器迭代。

但是在繼承結(jié)構(gòu)上,ArrayList繼承了AbstractList。這是java中通用的一種技巧,由于java8之前的接口無法指定默認(rèn)方法,如果你要實(shí)現(xiàn)List接口,你需要實(shí)現(xiàn)其中的所有方法。
但是,List接口中的所有方法是大而全的,有大量方法是為了方便調(diào)用者使用而設(shè)計(jì)的衍生方法,并非實(shí)現(xiàn)該接口的必須方法。比如說,isEmpty方法就可以化歸到size方法上。
所以,java對(duì)于List接口會(huì)提供一個(gè)AbstractList抽象類,里面提供了部分方法的默認(rèn)實(shí)現(xiàn),而繼承該類的只需要實(shí)現(xiàn)一組最小的必須方法即可。
總而言之一句話,AbstractList的作用是給List接口提供某些方法的默認(rèn)實(shí)現(xiàn)。

ArrayList源碼分析 屬性

先看ArrayList內(nèi)部保存的屬性和狀態(tài)。

    private static final int DEFAULT_CAPACITY = 10;
    private static final Object[] EMPTY_ELEMENTDATA = {};
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
    transient Object[] elementData; // non-private to simplify nested class access
    private int size;

其中,最關(guān)鍵的為elementDatasizeelementData也即真正存儲(chǔ)元素的java原生數(shù)組。
由于ArrayList中實(shí)際保存的元素個(gè)數(shù)是少于elementData數(shù)組的大小的,也即會(huì)有部分空間的浪費(fèi),因此size屬性是用來保存ArrayList存儲(chǔ)的元素個(gè)數(shù)。

值得注意的是,elementData沒有訪問修飾符,我們知道默認(rèn)是對(duì)包內(nèi)可見的。這樣做的目的是:

即能保證使用者(包外)無法訪問到這個(gè)屬性,保證了數(shù)據(jù)隱藏;

另外一方面,實(shí)現(xiàn)ArrayList需要一些輔助作用的嵌套類,它們需要知道部分ArrayList的實(shí)現(xiàn)細(xì)節(jié)。由于它們和ArrayList在同一個(gè)包下,這樣輔助類能夠訪問到它們。

構(gòu)造函數(shù)

構(gòu)造函數(shù)一共有三個(gè),下面是其中兩個(gè)。第三個(gè)是從其它容器初始化,沒什么好看的地方。

    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);
        }
    }

    /**
     * Constructs an empty list with an initial capacity of ten.
     */
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

第一個(gè)帶參數(shù)的構(gòu)造函數(shù),行為上這個(gè)都知道,創(chuàng)建出來的依然是個(gè)空表,看代碼也可發(fā)現(xiàn)執(zhí)行完后size屬性為0。initialCapacity的含義是指定內(nèi)部存儲(chǔ)數(shù)據(jù)的原生數(shù)組的初始化大小。
代碼邏輯很簡(jiǎn)單,initialCapacity大于0時(shí)對(duì)elementData分配該大小的原生數(shù)組。但是問題是,initialCapacity為0時(shí),并不是直接new一個(gè)大小為0的數(shù)組,而是使用靜態(tài)變量EMPTY_ELEMENTDATA來代替。
為什么?EMPTY_ELEMENTDATA是靜態(tài)變量,所有實(shí)例共享,個(gè)人猜測(cè)應(yīng)該是為了省點(diǎn)內(nèi)存吧??墒沁@個(gè)占不了多大的內(nèi)存啊,這個(gè)理由可能說服力不太強(qiáng)。

第二個(gè)默認(rèn)構(gòu)造函數(shù),這個(gè)函數(shù)行為上也是創(chuàng)建空表。但是會(huì)預(yù)先將存儲(chǔ)數(shù)據(jù)的原生數(shù)組的大小設(shè)置為DEFAULT_CAPACITY,也就是默認(rèn)值10。這樣,能避免在大小較小時(shí)頻繁擴(kuò)容帶來性能損耗。

按照這個(gè)思路,代碼應(yīng)該長(zhǎng)這樣才對(duì):

    public ArrayList() {
        this.elementData = new Object[DEFAULT_CAPACITY];
    }

但是實(shí)際上,我們發(fā)現(xiàn)DEFAULTCAPACITY_EMPTY_ELEMENTDATA其實(shí)是個(gè)空表,也是靜態(tài)變量,多實(shí)例共享的。
這實(shí)際上也可以認(rèn)為是一種優(yōu)化手段,因?yàn)楹芏鄨?chǎng)景都是直接默認(rèn)構(gòu)造的一個(gè)空表在哪里放著,為了節(jié)約內(nèi)存,jdk實(shí)現(xiàn)先不實(shí)際分配空間,僅僅做一個(gè)類似標(biāo)記作用的操作,之后真正使用了才會(huì)分配空間,達(dá)到一個(gè)類似“延遲分配”的效果。這些思路在擴(kuò)容相關(guān)的代碼中有所體現(xiàn)。

get和set

get和set沒什么好說的,實(shí)際上是直接操作內(nèi)部的原生數(shù)組。
不過,從下面的代碼中可以看到,在get和set之前,會(huì)做越界檢查。

    private void rangeCheck(int index) {
        if (index >= size)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }

    public E get(int index) {
        rangeCheck(index);

        return elementData(index);
    }

    E elementData(int index) {
        return (E) elementData[index];
    }

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

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

下面再來看插入元素。插入有兩種:

第一種是在末尾插入。

第二種是在中間插入。

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

    public void add(int index, E element) {
        rangeCheckForAdd(index);

        ensureCapacityInternal(size + 1);  // Increments modCount!!
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
        elementData[index] = element;
        size++;
    }

    private void rangeCheckForAdd(int index) {
        if (index > size || index < 0)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }

思路都特別顯然,主要是在操作之前,調(diào)用了ensureCapacityInternal函數(shù),這個(gè)函數(shù)調(diào)用完畢后會(huì)保證內(nèi)部數(shù)組的空間能存儲(chǔ)下這么多元素。
此外,void add(int, E)函數(shù)還做了越界檢查。

擴(kuò)容操作

接下來看ensureCapacityInternal函數(shù),相關(guān)實(shí)現(xiàn)涉及到四個(gè)函數(shù)。
這個(gè)函數(shù)的目的是保證執(zhí)行完后,內(nèi)部的原生數(shù)組至少能容納minCapacity個(gè)元素。
之前所說的那種“延遲分配”操作在這里就體現(xiàn)出來了。分析代碼流程不難發(fā)現(xiàn),當(dāng)elementDataDEFAULTCAPACITY_EMPTY_ELEMENTDATA,也即前面所說到的那個(gè)標(biāo)記,那么之后擴(kuò)容后的原生數(shù)組空間一定不小于DEFAULT_CAPACITY,也就是前面定義處的10。

    private void ensureCapacityInternal(int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }

        ensureExplicitCapacity(minCapacity);
    }

    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

    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);
    }

    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }

再看具體的擴(kuò)容邏輯,也即grow函數(shù)。邏輯還不單一。首先擴(kuò)容嘗試按照原來大小的1.5倍擴(kuò)容,為了性能,這里除以2優(yōu)化成了右移運(yùn)算。
如果1.5倍不夠怎么辦?如果是我,我可能會(huì)優(yōu)雅的遞歸再擴(kuò)大,然而,jdk的做法是如果1.5倍不夠的話直接按照需要的大小擴(kuò)容。
最后,如果實(shí)在太大,也要做一下限制,最大可達(dá)到的大小為Integer.MAX_VALUE,否則就超過了int的范圍,溢出了。

移除操作

下面是移除相關(guān)的函數(shù),有兩個(gè):

E remove(int)是通過索引移除。

boolean remove(Object)是通過元素移除。

    public E remove(int index) {
        rangeCheck(index);

        modCount++;
        E oldValue = elementData(index);

        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

        return oldValue;
    }

    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;
    }

    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
    }

按索引移除的的思路很簡(jiǎn)單,首先把需要移除的元素拿出來,然后后面的都往前挪一個(gè),通過數(shù)據(jù)拷貝實(shí)現(xiàn)。
但是,有個(gè) 特別需要注意的地方是,假設(shè)刪除之后的表大小為N,往前挪一個(gè)后,elementData[N - 1]elementData[N]都指向了最后一個(gè)元素。這時(shí)elementData[N]仍然持有對(duì)該元素的引用。如果之后試圖移除表中最后一個(gè)元素,它可能不會(huì)及時(shí)的被gc干掉,造成無意義的額外內(nèi)存占用。

按元素移除的思路也很顯然,先是找到該元素的索引,然后按索引移除。fastRemove的代碼幾乎和remove(int)一模一樣,不知道為什么復(fù)用一下。。。

最后,從代碼可以看到一點(diǎn),表內(nèi)元素會(huì)被移除,但是jdk對(duì)ArrayList的實(shí)現(xiàn)只會(huì)擴(kuò)容表,而不會(huì)縮小表以減小內(nèi)存占用。不過,它提供了一個(gè)trimToSize方法,將表中原生數(shù)組的空閑空間去掉:

    public void trimToSize() {
        modCount++;
        if (size < elementData.length) {
            elementData = (size == 0)
              ? EMPTY_ELEMENTDATA
              : Arrays.copyOf(elementData, size);
        }
    }

很明顯,它重新分配一個(gè)正好合適的原生數(shù)組,然后拷貝過去。

removeAll&retainAll

這兩個(gè)函數(shù)很好理解:

removeAll是移除所有c中的元素

retainAll是保留所有c中的元素

這兩個(gè)函數(shù)算法幾乎一模一樣,因此抽象出一個(gè)函數(shù)batchRemove來實(shí)現(xiàn)。

    public boolean removeAll(Collection c) {
        Objects.requireNonNull(c);
        return batchRemove(c, false);
    }

    public boolean retainAll(Collection c) {
        Objects.requireNonNull(c);
        return batchRemove(c, true);
    }

    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;
    }

核心在于try中的那三行代碼。這個(gè)算法簡(jiǎn)化了就是這樣:

有兩個(gè)數(shù)組data和isRemove,isRemove[i]為true表示data[i]應(yīng)該被移除。
要求在O(n)時(shí)間復(fù)雜度,O(1)空間復(fù)雜度,將data中isRemove[i]為true的元素移除。

算法這種東西,思路我能在腦子里想象出來畫面,但是說不清楚。。。 大學(xué)里學(xué)習(xí)算法時(shí)候都寫過,就不多說了。

其它

感覺寫的有點(diǎn)多。。。以上是和ArrayList操作密切相關(guān)的,其它的簡(jiǎn)單總結(jié)下吧。

subList. subList返回的是一個(gè)List,代表著該ArrayList的中間一段。這個(gè)subList實(shí)際上返回的是類似視圖的對(duì)象,它本身不存儲(chǔ)數(shù)據(jù),所有的對(duì)subList的操作最終會(huì)反映到原來的那個(gè)ArrayList上來。實(shí)現(xiàn)在輔助類SubList中。

iterator. 返回迭代器,與之有一個(gè)搭配的輔助類Itr?;径际且恍┌b代碼。

sort. 排序是通過調(diào)用Arrays.sort實(shí)現(xiàn)的,所以,具體的排序算法要看Arrays.sort。

還有一個(gè)沒有看懂的問題,即在AbstractList中有一個(gè)modCount字段,ArrayList的實(shí)現(xiàn)中多次操作該字段。但是仍然沒有理解該字段的作用。

Vector

Vector提供的容器模型和ArrayList幾乎一模一樣,只不過它是線程安全的。
它的代碼思路上和ArrayList差不多,但是有一些實(shí)現(xiàn)細(xì)節(jié)上的小區(qū)別。

首先,它有一個(gè)參數(shù)capacityIncrement,能夠控制擴(kuò)容的細(xì)節(jié),看構(gòu)造函數(shù):

    public Vector(int initialCapacity, int capacityIncrement) {
        super();
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        this.elementData = new Object[initialCapacity];
        this.capacityIncrement = capacityIncrement;
    }

    public Vector(int initialCapacity) {
        this(initialCapacity, 0);
    }

如果不指定的話,這個(gè)initialCapacity默認(rèn)值為0。在內(nèi)部使用屬性capacityIncrement保存。

其次,再看控制擴(kuò)容的核心函數(shù)grow,研究下擴(kuò)容邏輯是不是有什么差異:

    private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
                                         capacityIncrement : oldCapacity);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

可以發(fā)現(xiàn),capacityIncrement控制的是擴(kuò)容的步長(zhǎng)。
如果沒有指定步長(zhǎng),那么則是按照兩倍擴(kuò)容,這是與ArrayList不同的地方。

總體上,它相當(dāng)于synchronized同步過的ArrayList,也即它對(duì)線程安全的實(shí)現(xiàn)非常暴力,并未用到太多的技巧。很顯然,在并發(fā)環(huán)境下,對(duì)vector的操作直接鎖住整個(gè)vector,相當(dāng)于操作vector的線程是串行操作vector,性能不高。

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

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

相關(guān)文章

  • JDK源碼(容器篇)

    摘要:三系列用于保存鍵值對(duì),無論是,還是已棄用的或者線程安全的等,都是基于紅黑樹。是完全基于紅黑樹的,并在此基礎(chǔ)上實(shí)現(xiàn)了接口??梢钥吹?,只有紅黑樹,且紅黑樹是通過內(nèi)部類來實(shí)現(xiàn)的。 JDK容器 前言 閱讀JDK源碼有段時(shí)間了,準(zhǔn)備以博客的形式記錄下來,也方便復(fù)習(xí)時(shí)查閱,本文參考JDK1.8源碼。 一、Collection Collection是所有容器的基類,定義了一些基礎(chǔ)方法。List、Se...

    Soarkey 評(píng)論0 收藏0
  • Week 2 - Java 容器 - 詳細(xì)剖析 List 之 ArrayList, Vector,

    摘要:底層使用的是雙向鏈表數(shù)據(jù)結(jié)構(gòu)之前為循環(huán)鏈表,取消了循環(huán)??焖匐S機(jī)訪問就是通過元素的序號(hào)快速獲取元素對(duì)象對(duì)應(yīng)于方法。而接口就是用來標(biāo)識(shí)該類支持快速隨機(jī)訪問。僅僅是起標(biāo)識(shí)作用。,中文名為雙端隊(duì)列。不同的是,是線程安全的,內(nèi)部使用了進(jìn)行同步。 前言 學(xué)習(xí)情況記錄 時(shí)間:week 2 SMART子目標(biāo) :Java 容器 記錄在學(xué)習(xí)Java容器 知識(shí)點(diǎn)中,關(guān)于List的需要重點(diǎn)記錄的知識(shí)點(diǎn)。...

    MartinDai 評(píng)論0 收藏0
  • List集合就這么簡(jiǎn)單【源碼剖析】

    摘要:線程不安全底層數(shù)據(jù)結(jié)構(gòu)是鏈表。的默認(rèn)初始化容量是,每次擴(kuò)容時(shí)候增加原先容量的一半,也就是變?yōu)樵瓉淼谋秳h除元素時(shí)不會(huì)減少容量,若希望減少容量則調(diào)用它不是線程安全的。 前言 聲明,本文用得是jdk1.8 前一篇已經(jīng)講了Collection的總覽:Collection總覽,介紹了一些基礎(chǔ)知識(shí)。 現(xiàn)在這篇主要講List集合的三個(gè)子類: ArrayList 底層數(shù)據(jù)結(jié)構(gòu)是數(shù)組。線程不安全 ...

    cpupro 評(píng)論0 收藏0
  • 這幾道Java集合框架面試題在面試中幾乎必問

    摘要:若遇到哈希沖突,則將沖突的值加到鏈表中即可。之后相比于之前的版本,之后在解決哈希沖突時(shí)有了較大的變化,當(dāng)鏈表長(zhǎng)度大于閾值默認(rèn)為時(shí),將鏈表轉(zhuǎn)化為紅黑樹,以減少搜索時(shí)間。有序,唯一紅黑樹自平衡的排序二叉樹。 本文是最最最常見Java面試題總結(jié)系列第三周的文章。主要內(nèi)容: Arraylist 與 LinkedList 異同 ArrayList 與 Vector 區(qū)別 HashMap的底層...

    bigdevil_s 評(píng)論0 收藏0
  • Java集合總結(jié)【面試題+腦圖】,將知識(shí)點(diǎn)一網(wǎng)打盡!

    摘要:而在集合中,值僅僅是一個(gè)對(duì)象罷了該對(duì)象對(duì)本身而言是無用的。將這篇文章作為集合的總結(jié)篇,但覺得沒什么好寫就回答一些面試題去了,找了一會(huì)面試題又覺得不夠系統(tǒng)。 前言 聲明,本文用的是jdk1.8 花了一個(gè)星期,把Java容器核心的知識(shí)過了一遍,感覺集合已經(jīng)無所畏懼了??!(哈哈哈....),現(xiàn)在來總結(jié)一下吧~~ 回顧目錄: Collection總覽 List集合就這么簡(jiǎn)單【源碼剖析】 Ma...

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

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

0條評(píng)論

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