從知識圖譜講到源碼實例,基礎的知識深刻學習就不僅是基礎

 關注公衆號:Java架構師聯盟,每日更新技術好文java

部分資料已經上傳到個人git倉庫中:有須要的能夠下載node

https://gitee.com/biwangsheng/mxqgit

做爲一個程序員,一般,咱們的程序須要根據程序運行時才知道建立多少個對象。但若非程序運行,程序開發階段,咱們根本不知道到底須要多少個數量的對象,甚至不知道它的準確類型。爲了知足這些常規的編程須要,咱們要求能在任什麼時候候,任何地點建立任意數量的對象,而這些對象用什麼來容納呢?咱們首先想到了數組,可是數組只能放同一類型的數據,並且其長度是固定的,那怎麼辦呢?集合便應運而生了!程序員

而對於集合的相關內容,我經過思惟導圖整理了一下,分紅兩個部分,Map和Collection兩個部分,來看一下面試

Map

 

Collection

 

由於篇幅緣由,只展現Collection的List,這也是在平常開發中應用最多的,或者說在面試過程當中被問到最多的一個知識點,由於它能夠擴充出來不少其餘的知識點編程

1、ArrayList的底層實現

  • ArrayList實現與List、RandomAccess接口,是順序接口,即元素存放的數據與放進去的順序相同,容許放入null元素,也支持隨機訪問數組

  • 底層經過數組實現。除該類未實現同步外,其他跟Vector大體相同數據結構

  • ArrayList至關於動態數據,其中最重要的兩個屬性分別是:elementData數組以及siz架構

 2、ArrayList能夠實現同步嗎

爲了追求效率,ArrayList沒有實現同步(synchronizd),若是須要逗哥線程併發訪問,用戶能夠手動同步,也可使用Vector代替。如能夠先採用Collections.synchronizedList()方法對其進行包裝併發

3、ArrayList的add()方法

在調用add()方法的時候首先進行擴容校驗,將插入的值放在尾部,並將size+1.

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

4、ArrayList的add(index,e)方法

若是調用add(index,e)在指定位置添加的話也是首先擴容校驗,接着對數據進行復制,目的是把index位置空出來放本次插入的數據,並將後面的數據向後移動一個位置。

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

也是一個數組複製的過程。

注意:

因而可知ArrayList的主要消耗時數組擴容以及在指定位置添加數據,在平常使用時最好是指定大小,儘可能減小擴容。更要減小在指定位置插入數據的操做。

5、ArrayList的序列化

因爲ArrayList是基於動態宿主實現的,因此並非全部的空間都被使用。所以使用了transient修飾。能夠防止被自動序列化

transient Object[] elementData; // non-private to simplify nested class access

所以ArrayList自定義了序列化和反序列化

//序列化 private void writeObject(java.io.ObjectOutputStream s)        throws java.io.IOException{        // Write out element count, and any hidden stuff        int expectedModCount = modCount;        s.defaultWriteObject();        // Write out size as capacity for behavioural compatibility with clone()        s.writeInt(size);        // Write out all elements in the proper order.       //只序列化了被使用的數據        for (int i=0; i<size; i++) {            s.writeObject(elementData[i]);        }        if (modCount != expectedModCount) {            throw new ConcurrentModificationException();        }    } //反序列化 private void readObject(java.io.ObjectInputStream s)        throws java.io.IOException, ClassNotFoundException {        elementData = EMPTY_ELEMENTDATA;        // Read in size, and any hidden stuff        s.defaultReadObject();        // Read in capacity        s.readInt(); // ignored        if (size > 0) {            // be like clone(), allocate array based upon size not capacity            int capacity = calculateCapacity(elementData, size);            SharedSecrets.getJavaOISAccess().checkArray(s, Object[].class, capacity);            ensureCapacityInternal(size);            Object[] a = elementData;            // Read in all elements in the proper order.            for (int i=0; i<size; i++) {                a[i] = s.readObject();            }        }    }

當對象中自定義了writeObject和readObject方法時,JVM會調用這兩個自定義方法來實現序列化和反序列化

從實現中能夠看出ArrayList值序列化了被使用的數據

6、ArrayList和Vector的比較

Vector也會是實現List接口,底層數據結構和ArrayList相似,也是一個動態數組存放的數據,不過在add()方法的時候使用synchronized進行同步數據,可是開銷較大,因此Vector是一個同步容器並非併發容器。

add()方法

public synchronized boolean add(E e) {        modCount++;        ensureCapacityHelper(elementCount + 1);        elementData[elementCount++] = e;        return true;    }

add(index,e)方法:指定位置插入數據

public void add(int index, E element) {        insertElementAt(element, index);    } public synchronized void insertElementAt(E obj, int index) {        modCount++;        if (index > elementCount) {            throw new ArrayIndexOutOfBoundsException(index                                                     + " > " + elementCount);        }        ensureCapacityHelper(elementCount + 1);        System.arraycopy(elementData, index, elementData, index + 1, elementCount - index);        elementData[index] = obj;        elementCount++;    }

7、ArrayList的擴容機制

  • 假若有20個數據須要添加,那麼會分別在第一次的時候將ArrayList

  • 以後擴容會按照1.5倍增加,也就是當添加第11個數據的時候,ArrayList繼續擴容變爲10*1.5=15;

  • 當添加第16個數據時。繼續擴容15*1.5=22個

  • 不能超過int的最大值(231-1)-8

 

private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

 

8、LinkedList的底層實現

  • LinkedList底層是基於雙向鏈表實現的;

  • 自己實現了List和Deque(雙端隊列)接口

  • 擁有List的一些特性(jdk1.7/1.8以後取消了循環,修改成雙向鏈表)

既能夠看作一個順序容器,又能夠看作一個隊列(Queue),同時又能夠看作一個棧(stack)。這樣看來,LinkedList簡直是個全能冠軍。當你須要使用棧或者隊列時,能夠考慮使用LinkedList,一方面是由於Java官方已經聲明不建議使用Stack類,更遺憾的是,Java里根本沒有一個叫作Queue的類(它是一個接口名)。關於棧或者隊列,如今的首選是ArrayDeque,它有着比LinkedList(當作棧或者隊列使用時)更好的性能。

9、LinkedList的add()方法

public boolean add(E e) {        linkLast(e);        return true;    } void linkLast(E e) {        final Node<E> l = last;        final Node<E> newNode = new Node<>(l, e, null);        last = newNode;        if (l == null)            first = newNode;        else            l.next = newNode;        size++;        modCount++;    }

可見每次插入都是移動指針,和ArrayList的拷貝數組來講效率要高上很多

10、LinkedList的查詢方法get(index)

public E get(int index) {        checkElementIndex(index);        return node(index).item;    } Node<E> node(int index) {        // assert isElementIndex(index);        if (index < (size >> 1)) {            Node<E> x = first;            for (int i = 0; i < index; i++)                x = x.next;            return x;        } else {            Node<E> x = last;            for (int i = size - 1; i > index; i--)                x = x.prev;            return x;        }    }

上述代碼,利用了雙向鏈表的特性,若是index離鏈表頭比較近,就從節點頭部遍歷。不然就從節點尾部開始遍歷。使用空間(雙向鏈表)來換取時間

  • node會以O(n/2)的性能去獲取一個節點

  • 若是索引值大於鏈表大小的一半,那麼將從尾節點開始遍歷,這樣的效率是很是低的,特別是當index越接近size的中間值

11、LinkedList優缺點

LinkedList插入、刪除都是移動指針效率很高

產訊須要遍歷進行查詢,效率較低

 

怎麼樣,不知道總結的詳細不,看完以後有沒有一點收穫,而這一些知識的整理也參考了下面這份文檔的內容

 

詳細內容展現

 

Set

 

List

 

 

 

map

 

而這份文檔除了集合以外,還包括這些知識,我想不算所有,這裏面大部分對如今的你都應該有所幫助吧

 

你比別人強的地方,不是你作過多少年的CRUD工做,而是你比別人掌握了更多深刻的技能。不要總停留在CRUD的表面工做,理解並掌握底層原理並熟悉源碼實現,並造成本身的抽象思惟能力,作到靈活運用,纔是你突破瓶頸,脫穎而出的重要方向!

你在刷抖音,玩遊戲的時候,別人都在這裏學習,成長,提高,人與人最大的差距其實就是思惟。你可能不信,優秀的人,老是在一塊兒。

 

相關文章
相關標籤/搜索