數據結構與算法——數組

1、概念

數組(Array)是一種線性表數據結構。它用一組連續的內存空間,來存儲一組具有相同類型的數據。

2、特點

2.1、線性表

​ 線性表就是數據排成像一條線一樣的結構。每個線性表上的數據最多隻有前和後兩個方向。其實除了數組,鏈表、隊列、棧等也是線性表結構。

2.2、內存空間連續,數據類型相同

=+當前元素地址=數組首地址+元素字節大小\ast數組下標

2.3、高效的隨機訪問,低效的插入和刪除

由於內存空間連續,訪問可以通過一次尋址找到對應數據,而插入和刪除後需要將後續元素進行多次數據搬移

訪問的時間複雜度:O(1)

插入和刪除的時間複雜度:O(n)

3、數組和容器對比

3.1、容器優勢

封裝了很多數組操作的方法,便於開發

支持動態擴容

3.2、數組優勢

可以存儲基本類型

表示多維數組更加直觀

4、其他

4.1、數組下標爲何從零開始

爲了方便隨機訪問(計算元素內存地址),如果下標從1開始會多做一次減法操作

4.2、手撕數組

package com.company;

import java.util.Arrays;

/**
 * 功能描述:
 *
 * @author liuchaoyong
 * @version 1.0
 * @date 2020/1/9 11:18
 */
public class MyArrayList<E> {

    /**
     * 空數組
     */
    private static final Object[] EMPTY_ELEMENTDATA = {};

    /**
     * 默認長度的空數組,用於區分當前ArrayList是默認構造函數創建的,還是有參構造函數創建的,
     * 主要是爲了提升擴容效率
     */
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    /**
     * 數組默認大小
     */
    private static final int DEFAULT_CAPACITY = 10;

    /**
     * 緩存數組
     */
    transient Object[] elementData;

    /**
     * 數組最大長度(由於數組是對象,包含對象頭信息,不超過8個字節,
     * 所以數組實際佔用的空間要比實際大一點,故-8)
     */
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

    /**
     * ArrayList大小,注意是elementData中實際存在的元素個數,
     * 而不是elementData的實際長度
     */
    private int size;

    /**
     * 無參構造函數會創建一個默認長度的空數組
     */
    public MyArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

    /**
     * 此有參構造函數創建一個指定長度的數組
     * >0創建指定長度的數組
     * =0指向空數組
     * <0拋出異常
     */
    public MyArrayList(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);
        }
    }

    /**
     * 獲取指定下標元素
     */
    public E get(int index) {
        rangeCheck(index);
        return elementData(index);
    }

    /**
     * 替換指定下標元素,並返回舊元素
     */
    public E set(int index, E element) {
        rangeCheck(index);
        E oldValue = elementData(index);
        elementData[index] = element;
        return oldValue;
    }

    /**
     * 向數組末尾添加元素
     */
    public boolean add(E element) {
        ensureCapacityInternal(size + 1);
        elementData[size++] = element;
        return true;
    }

    /**
     * 向指定下標添加元素
     */
    public void add(int index, E element) {
        rangeCheckForAdd(index);
        ensureCapacityInternal(size + 1);
        //將index及其後面位置的元素整體從index+1開始覆蓋
        System.arraycopy(elementData, index, elementData, index + 1,
                size - index);
        elementData[index] = element;
        size++;
    }

    /**
     * 移除指定下標元素並返回
     */
    public E remove(int index) {
        rangeCheck(index);
        E oldValue = elementData(index);
        int numMoved = size - index - 1;
        //將index+1及其後面位置的元素整體從index開始覆蓋,並將末尾元素置空觸發gc回收
        if (numMoved > 0) {
            System.arraycopy(elementData, index + 1, elementData, index, numMoved);
        }
        elementData[--size] = null;
        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;
    }

    /**
     * 判斷數組中是否包含某元素
     */
    public boolean contains (Object o){
        return indexOf(o) >= 0;
    }

    /**
     * 獲取數組中第一次出現某元素的下標
     */
    public int indexOf(Object o) {
        if (o == null) {
            for (int index = 0; index < size; index++) {
                if (elementData[index] == null) {
                    return index;
                }
            }
        } else {
            for (int index = 0; index < size; index++) {
                if (o.equals(elementData[index])) {
                    return index;
                }
            }
        }
        return -1;
    }

    /**
     * 獲取數組中最後一次出現某元素的下標
     */
    public int lastIndexOf(Object o){
        if (o == null) {
            for (int index = size-1; index > 0; index--) {
                if (elementData[index] == null) {
                    return index;
                }
            }
        } else {
            for (int index = size-1; index > 0; index--) {
                if (o.equals(elementData[index])) {
                    return index;
                }
            }
        }
        return -1;
    }

    /**
     * 獲取ArrayList大小
     */
    public int size() {
        return size;
    }

    /**
     * 判斷ArrayList是否爲空
     */
    public boolean isEmpty() {
        return size == 0;
    }

    /**
     * 清空數組
     */
    public void clear() {
        for (int index = 0; index < size; index++) {
            elementData[index] = null;
        }
        size = 0;
    }

    /**
     * 數組越界檢查
     */
    private void rangeCheck(int index) {
        if (index >= size) {
            throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);
        }
    }

    /**
     * 數組越界檢查
     */
    private void rangeCheckForAdd(int index) {
        //rangeCheck沒有
        //System.arraycopy()有性能消耗,在此攔截index<0防止異常輸入無謂地消耗性能
        if (index > size || index < 0) {
            throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);
        }
    }

    /**
     * 獲取指定下標元素
     */
    @SuppressWarnings("unchecked")
    private E elementData(int index) {
        return (E) elementData[index];
    }

    /**
     * 快速移除指定下標元素,沒有越界校驗,也沒有返回值
     */
    private void fastRemove(int index) {
        int numMoved = size - index - 1;
        if (numMoved > 0) {
            System.arraycopy(elementData, index + 1, elementData, index, numMoved);
        }
        elementData[--size] = null;
    }

    /**
     * 擴容校驗
     */
    private void ensureCapacityInternal(int minCapacity) {
        if(elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA){
            minCapacity = Math.max(DEFAULT_CAPACITY,minCapacity);
        }
        if (minCapacity - elementData.length > 0) {
            grow(minCapacity);
        }

    }

    /**
     * 緩存數組擴容
     */
    private void grow(int minCapacity) {
        //舊數組長度
        int oldCapacity = elementData.length;
        //擴容1.5倍的新數組長度(要說爲啥計算不乘1.5,而用移位,因爲從硬件上看移位更容易操作更快呀)
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        //如果舊數組長度足夠大,計算出的新數組長度有可能發生整型溢出變爲負數,做一下數據修正
        if (newCapacity - minCapacity < 0) {
            newCapacity = minCapacity;
        }
        //如果舊數組長度足夠大,新數組長度超出上限,則給新數組申請最大長度
        if (newCapacity - MAX_ARRAY_SIZE > 0) {
            if (minCapacity < 0) {
                throw new OutOfMemoryError();
            }
            //如果數組需要的長度比最大限制還要大,則嘗試申請Integer.MAX_VALUE長度的數組(失敗會報內存溢出),否則把最大限制長度賦給新數組
            newCapacity = minCapacity > MAX_ARRAY_SIZE ? Integer.MAX_VALUE : MAX_ARRAY_SIZE;
        }
        Object[] copy = new Object[newCapacity];
        System.arraycopy(elementData, 0, copy, 0,
                elementData.length);
        elementData = copy;
    }

}
發佈了61 篇原創文章 · 獲贊 13 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章