1、概念
數組(Array)是一種線性表數據結構。它用一組連續的內存空間,來存儲一組具有相同類型的數據。
2、特點
2.1、線性表
線性表就是數據排成像一條線一樣的結構。每個線性表上的數據最多隻有前和後兩個方向。其實除了數組,鏈表、隊列、棧等也是線性表結構。
2.2、內存空間連續,數據類型相同
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;
}
}