动态数组(Dynamic Array) -> ArrayList 方法、源码解析
前言:在java中,常使用ArrayList来做数据的暂存、处理,我们都知道,ArrayList实现了接口List,List接口继承了Collection接口。Collection是所有集合类的父类。ArrayList使用非常广泛,不论是数据库表查询,excel导入解析,还是网站数据爬取都需要使用到,了解ArrayList原理及使用方法显得非常重要。
如下图👇,ArrayList是一种常见的线性的数据结构
一、 在内存中,ArrayList的存储结构是怎样的?
一张图你就懂了👇
对象名放在栈中,new出的数组放在堆中。数组中存储对象的地址
二、那么,ArrayList内部结构是如何实现的呢??
首先,让我们开始回忆一下我们在使用ArrayList的时候常用的属性和方法
ArrayList是一个可以存储Object(任何)类型的对象,所以我们给它加上泛型
- int size(); // 元素的数量
- boolean isEmpty(); // 是否为空
- boolean contains(E element); // 是否包含某个元素
- void add(E element); // 添加元素到最后面
- E get(int index); // 返回index位置对应的元素
- E set(int index, E element); // 设置index位置的元素
- void add(int index, E element); // 往index位置添加元素
- E remove(int index); // 删除index位置对应的元素
- int indexOf(E element); // 查看元素的位置
- void clear(); // 清除所有元素
而我们看不到的有哪些呢??
1️⃣除了上述的属性,在ArrayList中还有我们不太注意的属性:capacity
、-1
即 ArrayList容量
、找不到元素时的返回值
。其中,capacity
是有默认值的,也可以进行在new的时候使用构造方法进行传值,自定义初始容量。
2️⃣另外,还有我们看不到的容量管理,这也是我们最需要注意,考察最多的一点就是扩容缩容机制:
1、扩容:每次添加元素以后,我们将进行扩容判断,当容量被被占满之前,会进行扩容操作。扩容的大小为原先容量的1.5倍(可以自己重写,JDK默认为1.5倍)。
2、缩容:每次删除元素,判断内部元素数量是否不足容量一半,不足则进行缩容。
接着就是ArrayList的实现啦→
为了理解和简洁,我们将继承List接口这一步省去。
public class ArrayList<E> {
/**
* 元素的数量
*/
private int size;
/**
* 所有的元素
*/
private E[] elements;
//默认初始容量
private static final int DEFAULT_CAPACITY = 10;
//元素未找到时的返回值
private static final int ELEMENT_NOT_FOUND = -1;
//带参的构造方法,指定初始容量
public ArrayList(int capaticy) {
capaticy = (capaticy < DEFAULT_CAPACITY) ? DEFAULT_CAPACITY : capaticy;
elements = (E[]) new Object[capaticy];
}
public ArrayList() {
this(DEFAULT_CAPACITY);
}
/**
* 清除所有元素
*/
public void clear() {
for (int i = 0; i < size; i++) {
elements[i] = null;
}
size = 0;
// 仅供参考 可自定义规则
if (elements != null && elements.length > DEFAULT_CAPACITY) {
elements = (E[]) new Object[DEFAULT_CAPACITY];
}
}
/**
* 元素的数量
* @return
*/
public int size() {
return size;
}
/**
* 是否为空
* @return
*/
public boolean isEmpty() {
return size == 0;
}
/**
* 是否包含某个元素
* @param element
* @return
*/
public boolean contains(E element) {
return indexOf(element) != ELEMENT_NOT_FOUND;
}
/**
* 添加元素到尾部
* @param element
*/
public void add(E element) {
add(size, element);
}
/**
* 获取index位置的元素
* @param index
* @return
*/
public E get(int index) {
rangeCheck(index);
return elements[index];
}
/**
* 设置index位置的元素
* @param index
* @param element
* @return 原来的元素ֵ
*/
public E set(int index, E element) {
rangeCheck(index);
E old = elements[index];
elements[index] = element;
return old;
}
/**
* 在index位置插入一个元素
* @param index
* @param element
*/
public void add(int index, E element) {
rangeCheckForAdd(index);
ensureCapacity(size + 1);
for (int i = size; i > index; i--) {
elements[i] = elements[i - 1];
}
elements[index] = element;
size++;
}
/**
* 删除index位置的元素
* @param index
* @return
*/
public E remove(int index) {
rangeCheck(index);
E old = elements[index];
for (int i = index + 1; i < size; i++) {
elements[i - 1] = elements[i];
}
elements[--size] = null;
trim();
return old;
}
/**
* 查看元素的索引
* @param element
* @return
*/
public int indexOf(E element) {
if (element == null) { // 1
for (int i = 0; i < size; i++) {
if (elements[i] == null) return i;
}
} else {
for (int i = 0; i < size; i++) {
if (element.equals(elements[i])) return i; // n
}
}
return ELEMENT_NOT_FOUND;
}
/**
* 保证要有capacity的容量
* @param capacity
*/
private void ensureCapacity(int capacity) {
int oldCapacity = elements.length;
if (oldCapacity >= capacity) return;
// 新容量为旧容量的1.5倍(1+0.5) JDK默认为1.5,这里可以自己指定
int newCapacity = oldCapacity + (oldCapacity >> 1);
E[] newElements = (E[]) new Object[newCapacity];
for (int i = 0; i < size; i++) {
newElements[i] = elements[i];
}
elements = newElements;
System.out.println(oldCapacity + "扩容为" + newCapacity);
}
/**
缩容
*/
private void trim() {
// 30
int oldCapacity = elements.length;
// 15
int newCapacity = oldCapacity >> 1;
if (size > (newCapacity) || oldCapacity <= DEFAULT_CAPACITY) return;
// 剩余空间还很多
E[] newElements = (E[]) new Object[newCapacity];
for (int i = 0; i < size; i++) {
newElements[i] = elements[i];
}
elements = newElements;
System.out.println(oldCapacity + "缩容为" + newCapacity);
}
private void outOfBounds(int index) {
throw new IndexOutOfBoundsException("Index:" + index + ", Size:" + size);
}
private void rangeCheck(int index) {
if (index < 0 || index >= size) {
outOfBounds(index);
}
}
private void rangeCheckForAdd(int index) {
if (index < 0 || index > size) {
outOfBounds(index);
}
}
@Override
public String toString() {
// size=3, [99, 88, 77]
StringBuilder string = new StringBuilder();
string.append("size=").append(size).append(", [");
for (int i = 0; i < size; i++) {
if (i != 0) {
string.append(", ");
}
string.append(elements[i]);
}
string.append("]");
return string.toString();
}
}