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