數據結構---數組

線性表,顧名思義就像一條線一樣,線性表是有序的,這個“有序”不是從小到大之類的概念。當然與之對應就是散列表,散就是亂的,無序的。JavaListSet,我們遍歷List每次結果都是一樣,這就是所謂的有序,遍歷Set,每次出來的結果可能都不一樣,這就是無序的。

 

數組是一種相同數據類型的元素組成的集合,是一種線性表,同樣屬於線性表結構的還有鏈表,隊列,棧。

數組在內存需要連續的空間來存放,這就是爲什麼申明數組的時候一定要設置大小,因爲只有設置了大小,才知道這個數組需要多大的內存空間,才能去內存中尋找大小合適的空間存放。正是因爲它是連續的,而且都是有下標索引的,所以具有很好的隨機訪問性。

爲什麼數組具有很好的隨機訪問性?爲什麼數據可以根據下標訪問?爲什麼下標要從0開始?

這就要說一下數組存儲結構。

比如int[] arr = new int[2] 這個數組去內存開闢空間的時候,

假如arr在內存的位置是從1000開始的,那麼

 

Arr[0] 在內存中的位置是   1000

Arr[1] 在內存中的位置是   1000 + arr[0]數據大小

Arr[2] 在內存中的位置是   1000 + arr[0]數據大小 + arr[1]數據大小

 

因爲數組都是同一種數據類型所以每個元素的數據大小是一樣的,換做如下表示就更清晰了

 

Arr[0] 在內存中的位置是   1000 + 0*dataSize(元素數據大小,比如int類型4個字節,long8個字節,對應在內存中的大小)

Arr[1] 在內存中的位置是   1000 + 1*dataSize

Arr[2] 在內存中的位置是   1000 + 2*dataSize

 

這樣一來是不是每個元素的位置就剛好是索引位置乘以數據大小,這樣我們就可以根據下標直接定位元素位置了。而且因爲它是連續的空間,所以可以藉助CPU的緩存機制,預讀數據,索引訪問效率更高。

但是有利就有弊,當我們想要插入和刪除的時候,爲了保證連續性,會做很大的搬遷工作。

而且容量有限,還需要注意數組越界問題。

當然如果數組長度聲明的過大也會造成空間浪費。Java中的ArrayList就是用數組實現的,正因爲如此,所以《effective java》中建議我們聲明ArrayList預估容量,創建時就指定合適的容量,因爲如果發生擴容,需要重新去創建一個更大的數組來存放,要尋找新的內存空間,一個是耗時,二來還要將原來數組的數據進行搬遷。

ArrayList是對數組進行了封裝,提供更多的操作。如果不確定數據多少的情況下肯定選用ArrayList,如果確定了長度,而且有十分在乎性能,那就用數組。

簡單實現一個自己的ArrayList,便於理解按照自己的邏輯寫,並沒有處理越界那些問題,和JDK的ArrayList裏面的方法邏輯是有出入的:

 

package com.nijunyang.algorithm.list;
/**
 * Description:
 * Created by nijunyang on 2020/3/30 23:25
 */
public class MyArrayList<E>{

    private static final int DEFAULT_SIZE = 10;
    /**
     * 數據(數組存放)
     */
    private Object[] elements;
    /**
     * 當前存放到數組的index
     */
    private int currentIndex;
    /**
     * 總容量大小(數組長度)
     */
    private int size;

    public MyArrayList() {
        this.elements = new Object[DEFAULT_SIZE];
        this.size = DEFAULT_SIZE;
    }

    public MyArrayList(int size) {
        this.elements = new Object[size];
        this.size = size;
    }

    public void add(E element) {
        elements[currentIndex++] = element; //後++是先賦值了之後再++
        //放到最後就進行擴容 容量翻倍
        if (currentIndex == size) {
            this.size = this.size * 2 ;
            Object newData[] = new Object[this.size];
            for (int i = 0; i < elements.length; i++) {
                newData[i] = elements[i];
            }
            this.elements = newData;
        }
    }

    /**
     * 按索引移除
     * @param index
     */
    public void remove(int index) {
        if (index >= 0 && index < currentIndex) {
            //後面的元素來覆蓋當前這個元素,然後後面的全部前移
            for (int j = index; j < this.elements.length - 1; j++) {
                elements[j] = elements[j + 1];
                //null 就後不用再移動了
                if (elements[j] == null) {
                    break;
                }
            }
            this.currentIndex--;
        }
    }

    public E get(int index) {
        if (index >= 0 && index < currentIndex) {
            return (E) this.elements[index];
        }
        return null;
    }

    public int size() {
        return currentIndex;
    }

    public String toString() {
        if (currentIndex == 0) {
            return "[]";
        }
        StringBuilder sb = new StringBuilder();
        sb.append('[');
        for (int i = 0; i < currentIndex; i++) {
            if (i == currentIndex - 1) {
                sb.append(elements[i].toString());
                sb.append(']');
                break;
            }
            sb.append(elements[i].toString());
            sb.append(',');
        }
        return sb.toString();
    }

    public static void main(String[] args) {
        MyArrayList<Integer> myArrayList = new MyArrayList<>(2);
        myArrayList.add(0);
        myArrayList.add(1);
        myArrayList.add(2);
        System.out.println(myArrayList.get(0));
        System.out.println(myArrayList.size());
        System.out.println(myArrayList);
        myArrayList.remove(1);
        System.out.println(myArrayList.get(0));
        System.out.println(myArrayList.size());
        System.out.println(myArrayList);
    }
}

 

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