數組和鏈表

一. 數組在內存中是如何存儲的?

數組是一種線性數據結構,用來存儲 相同數據類型 的一組數據。當創建一個數組的時候必須確定它的大小,系統會在內存中開闢一塊連續的內存空間來保存數組,因此數組的容量固定且無法動態改變(空間效率不高)。如果只是定義一個數組變量(引用類型)如 int[] arr,系統僅在棧內存中定義了一個空引用 arr,這個引用並未指向任何有效的內存(內存指的是堆內存中的數據)。數組初始化後 arr = new int[6]; 系統會在堆內存爲其分配一塊連續的內存空間,此時數組每個元素的默認值都是0,因爲例子中定義的是int類型的數組。棧中存儲的數組名存的是堆中數組第一個元素的首地址,與 arr[0] 等價。

數組可以存儲基本類型數據和引用類型數據。存儲基本數據類型時,每個數組元素裏存儲的是基本類型數據;存儲引用類型時,每個數組元素裏存儲的還是引用,它指向另一塊內存。

擴:在方法中定義的一些基本類型的遍歷和對象的引用變量都在方法的棧內存中分配空間存儲。

       堆內存用來存放 new 出來的對象和數組,在堆內存中分配的內存由 java 虛擬機的自動垃圾回收器來管理。

二. 鏈表

鏈表在內存中不是連續存儲的,可以充分利用內存的碎片空間。

三. ArrayList

3.1 ArrayList 底層是用數組實現的,相當於動態數組,但是擴容操作性能消耗比較大。(當數組容量不夠用時,創建一個比原數組容量大的新數組,將原數組的元素“搬到”新數組,再把新添加的元素也放入新數組,最後將新數組賦給原數組)

3.2 ArrayList 查詢效率高,增刪效率低。

原因:

ArrayList 插入和刪除元素時,除非插入和刪除的位置都在末尾,否則代碼的開銷會很大。加入要在下標爲 m 的位置插入一個元素,需要先將從下標爲 m 開始至末尾的所有元素依次向後移動一位,然後再在下標爲 m 的位置插入新的元素。刪除同理,刪除下標爲 m 的元素後,m 之後的所有元素需要向前移動一位。而查詢:因爲數組在內存上佔有一塊連續的內存空間,只需要通過下標即可直接拿到想要的元素。故ArrayList 查詢效率高,增刪效率低。

3.3 ArrayList 效率高,但是線程不安全,允許元素爲 null

從哪裏看出來 ArrayList 是線程不安全的呢?從源碼可以看出。

    /**
     * Appends the specified element to the end of this list.
     *
     * @param e element to be appended to this list
     * @return <tt>true</tt> (as specified by {@link Collection#add})
     */
    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!判斷是否需要擴容,如需要則擴容
        elementData[size++] = e;
        return true;
    }

① 造成下標溢出

假設數組 arr 的長度爲10,目前 arr 已經有了 9 個元素,此時線程 A 和線程 B 同時向數組 arr 中增加一個元素,

線程 A 進入 add 方法,此時 size == 9,ensureCapacityInternal 方法判斷容聯足夠,不需要擴容 ;

線程 B 得到同樣的結論;

線程 A 成功向數組添加一個元素,而線程 B 添加元素時則發生下標溢出異常(B 在調用 ensureCapacityInternal 方法時應該擴容而未擴容造成的)

② 一個線程添加的值覆蓋另一個線程添加的值

elementDate[size++] = e; 其實是分兩步執行的,如下:

elementDate[size] = e;

size++;

當 size == 0 時,線程 A 和線程 B 同時向 animal 添加元素,

線程 A 把新的元素 dog 放到 animal[0],此時還沒有執行 size++;

線程 B 又把新的元素 cat 放到 animal[0],就覆蓋了 線程 A 的添加的值;

線程 A 和 B 都執行完之後,animal[0]=cat , animal[1]=null,size=2

解決 ArrayList 的線程不安全的辦法是:藉助 Collections 裏面的 synchronizedList 方法,

List list = Collections.synchronizedList(new ArrayList());

但是非常消耗性能。如果想用線程安全的可以選擇 Vector。

四. LinkedList

Linkedlist 增刪效率高,查詢效率低

五. vector

六. HashTable

七. HashMap

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章