目录
ArrayList底层
原理
ArrayList底层是一个Object数组,通过对数组的扩容和缩减,让使用者感觉到ArrayList是一个自动大小集合,用起来十分方便,解决了数组固定长度带来的不便。
扩容
Object数组的增长因子是20%,添加数据的时候,会根据Object数组实际保存数据长度size+要添加数据的长度是否大于当前Object数组实际的长度,如果大于说明需要扩容,因为当前数组已经存不下将要添加的数据了。
缩减
Object数组的缩减因子是50%,当移除数据时,发现Object数组实际保存数据的长度已小于或等于Object数组实际长度的50%,这个时候就会对Object数组进行,缩减操作,缩减到Object数组实际保存数据长度size的大小。为什么要缩减呢?不就是为了减少内存的开销,毕竟占着不用,太浪费资源了。
扩容和缩减需注意
扩容和缩减都会涉及到,要new一个新的Object数组,那么原来已存在的数组,不能置之不理。因为,原来的数组里面,每一个下标位置都还存放着指向对象的引用。一旦没有置NULL处理,假如当前Object数组要删除一些数据,删除后,当前Object数组,已经不再指向那些对象了,按理说那些对象要被当成垃圾,可是原有的数组没有把引用断掉,将导致已被当前object数组删除掉的对象不能及时被jvm当成垃圾,这样会造成内存被本该是垃圾的对象占用着,浪费系统资源。
System.arraycopy
jdk源代码
src:源数组
srcPos:源数组的下标位置(包含)
dest:目标数组
destPos:目标数组下标位置(包含)
length:源数组要移动数组长度
功能:将源数组下标位置srcPos至srcPos + length - 1之间下标的数据,移动到目标数组下标位置destPos至destPos + length - 1之间下标处,相当于整体把一个区间的数据,移动到另一个区间。
/*
* @param src the source array.
* @param srcPos starting position in the source array.
* @param dest the destination array.
* @param destPos starting position in the destination data.
* @param length the number of array elements to be copied
*/
public static native void arraycopy(Object src, int srcPos,
Object dest, int destPos,
int length);
说明
ArrayList源码里面大量使用了这个方法,这个方法是native修饰本地方法,是c语写的,源码在jdk里面看不到。这个方法是对数组进行整体移动,jdk都大量使的方法,肯定是好东西,所以有需用到这个方法的也可以使用。
为什么要用?
因为数组自身的缺陷,在插入和删除操作时,需要对数组向前或向后整体移动,数据量太大时,这样必然在性能方面会下降很多,jdk为什么不用java代码写这个方法呢?肯定是嫌弃效率太底,所以才使用c语言写这个方法,供jdk调用。
我为什么不用?
本着写源码态度,所以用java代码自已写了arraycopy,因为我们知道arraycopy的逻辑处理,自然写起来也不难。
Iterator迭代器
通过继承Iterator接口,实现接口的方法,来实现一个迭代器。迭代器不就是为了方便我们对集合进行遍历,同时实现了迭代器还能用增强for去遍历。
代码实现常用功能
首先定义了一个List接口,方法如下
方法 | 说明 |
int size(); | 当前数组实际存储数据元素的个数 |
default boolean isEmpty() { return size() == 0; } | 接口默认实现,判断集合是否为空 |
default boolean contains(T t) { return -1 != indexOf(t); } | 接口默认实现,判断一个值是否包含在集合中 |
boolean contains(List<T> list); | 判断另一个集合,所有元素是否包含在当前集合中 |
int indexOf(T t); | 从头开始寻找,根据一个值,获取值对应的下标值 |
int lastIndexOf(T t); | 从尾部开始寻找,根据一个值,获取值对应的下标值 |
void add(T t); | 将一个值添加到当前集合 |
void add(int index, T t); | 将一个值,添加到集合中指定下标位置 |
void add(List<T> list); | 将另一个集合所有元素,添加到本集合中 |
void add(int index, List<T> list); | 将另一个集合所有元素,添加到本集合指定下标位置 |
T get(int index); | 根据下标位置获取一个值 |
T set(int index, T t); | 将集合指定下标位置值,替换为新值t |
default void remove(T t) { remove(indexOf(t)); trimToSize(); } | 默认实现,将一个值从当前集合中移除 |
void remove(int index); | 将集合指定下标处值移除 |
void remove(List<T> list); | 将当前集合中包含list集合的值,移除 |
void remove(int fromIndex, int toIndex); | 将指定(包含)开始下标和(包含)结束下标之间值,从集合中移除 |
List<T> retain(List<T> list); | 当前集合中只保留list中存在的元素,相当于交集 |
List<T> subList(int fromIndex, int toIndex); | 获取一个子集合,根据(包含)开始下标位置和(包含)结束下标位置 |
void sort(Comparator<T> cmp); | 对集合进行排序 |
Object[] toArray(); | 将集合转化成数组 |
void clear(); | 清空集合 |
void trimToSize(); | 缩减集合 |
int capacity(); | 数组实际的长度 |
List接口代码
package com.wxl.practices.ArrayList;
import java.util.Comparator;
public interface List<T> extends Iterable<T> {
int size();
default boolean isEmpty() {
return size() == 0;
}
default boolean contains(T t) {
return -1 != indexOf(t);
}
boolean contains(List<T> list);
int indexOf(T t);
int lastIndexOf(T t);
void add(T t);
void add(int index, T t);
void add(List<T> list);
void add(int index, List<T> list);
T get(int index);
T set(int index, T t);
default void remove(T t) {
remove(indexOf(t));
trimToSize();
}
void remove(int index);
void remove(List<T> list);
void remove(int fromIndex, int toIndex);
List<T> retain(List<T> list);
List<T> subList(int fromIndex, int toIndex);
void sort(Comparator<T> cmp);
Object[] toArray();
void clear();
void trimToSize();
int capacity();
}
ArrayList类代码
package com.wxl.practices.ArrayList;
import java.util.Comparator;
import java.util.Iterator;
public class ArrayList<T> implements List<T> {
//数组增长因子
private final float INCREASE_FACTOR = 1.2f;
//数组的缩减因子
private final float DECREASE_FACTOR = 0.5f;
//一个空数组
private Object[] EMPTY = {};
//一个临时数组,数组增长或缩减时交换值时用
private Object[] temp = EMPTY;
//保存值的数组
private Object[] array;
//数组实际存储数据的大小
private int size;
//创建ArrayList对象时给数组赋值为空数组
public ArrayList() {
this.array = EMPTY;
}
/**
* 判断是否需要对数组时行扩容,如果需要则扩容,不需要不扩容
* 增长因子是20%
* @param needLength 当前数组要加入的元素
*/
private void ensureCapacity(int needLength) {
if (size + needLength > array.length) {
changeArray((int) Math.ceil((size + needLength) * INCREASE_FACTOR));
}
}
/**
* 当前数组实际存储数据的个数
* @return
*/
@Override
public int size() {
return size;
}
/**
* 判断List集合是否全部包含在当前集合
* 如果全部包含则返回true
* 只要有一个不包含就返回false
* @param list
* @return
*/
@Override
public boolean contains(List<T> list) {
Iterator<T> it = list.iterator();
while (it.hasNext()) {
if(!contains(it.next())) {
return false;
}
}
return true;
}
/**
* 根据值,查找值所在的下标
* @param t 值
* @param isForward 是否从前向后遍历true为从前向后,false为从后向前
* @return
*/
private int getIndex(T t, boolean isForward) {
InnerIterator it = new InnerIterator(isForward);
while (it.hasNext()) {
if(it.next().equals(t)) {
return it.getIndex();
}
}
return -1;
}
/**
* 从前向后查找集合中第一个和t相同的下标
* @param t
* @return
*/
@Override
public int indexOf(T t) {
return getIndex(t, true);
}
/**
* 从后向前查找集合中第一个和t相同的下标
* @param t
* @return
*/
@Override
public int lastIndexOf(T t) {
return getIndex(t, false);
}
/**
* 将t值添加到集合
* @param t
*/
@Override
public void add(T t) {
ensureCapacity(1);
array[size++] = t;
}
/**
* 将t值插入到指定的下标处
* @param index
* @param t
*/
@Override
public void add(int index, T t) {
checkIndex(index);
ensureCapacity(1);
arraycopy(array, index, array, index + 1, size - index);
array[index] = t;
size++;
}
/**
* 向集合中添加另一个集合
* @param list
*/
@Override
public void add(List<T> list) {
final int LEN = list.size();
ensureCapacity(LEN);
arraycopy(list.toArray(), 0, array, size, LEN);
size += LEN;
}
/**
* 在集合指定下标处添加另一个集合
* @param index
* @param list
*/
@Override
public void add(int index, List<T> list) {
checkIndex(index);
final int LEN = list.size();
ensureCapacity(LEN);
arraycopy(array, index, array, index + LEN, size - index);
arraycopy(list.toArray(), 0, array, index, LEN);
size += LEN;
}
/**
* 获取指定下标的值
* @param index
* @return
*/
@Override
public T get(int index) {
checkIndex(index);
return (T) array[index];
}
/**
* 将集合中指定下标的值添换为新t
* @param index
* @param t
* @return
*/
@Override
public T set(int index, T t) {
checkIndex(index);
T oldT = (T) array[index];
array[index] = t;
return oldT;
}
/**
* 将集合指定下标元素删除掉
* @param index
*/
@Override
public void remove(int index) {
checkIndex(index);
arraycopy(array, index + 1, array, index, size - index - 1);
array[--size] = null;
trimToSize();
}
/**
* 删除值当前集合中包含list的值,或不包含list的值,根据isContainsd确定
* @param list
* @param isContains
* @return
*/
private List<T> remove(List<T> list, boolean isContains) {
List<T> rstList = null;
if (!isContains) rstList = new ArrayList<>();
Iterator<T> it = new InnerIterator<T>(true);
T t;
while (it.hasNext()) {
t = it.next();
if((isContains ? list.contains(t) : !list.contains(t))) {
if (!isContains) rstList.add(t);
it.remove();
}
}
trimToSize();
return rstList;
}
/**
* 从当前集合中,删除当前集合包含list集合里面元素的值
* @param list
*/
@Override
public void remove(List<T> list) {
remove(list, true);
}
/**
* 从当前集合中删除,指定下标开始(包含)和结束(包含)位置的元素
* @param fromIndex
* @param toIndex
*/
@Override
public void remove(int fromIndex, int toIndex) {
checkIndex(fromIndex, toIndex);
final int LEN = toIndex - fromIndex + 1;
final int REMOVE_LEN = size - toIndex - 1;
arraycopy(array, toIndex + 1, array, fromIndex, REMOVE_LEN);
for (int i = fromIndex + REMOVE_LEN; i < size; i++) {
array[i] = null;
}
size -= LEN;
trimToSize();
}
/**
* 当前集合中只保留list里面的元素,不包含在list里面的元素全部删除
* @param list
* @return
*/
@Override
public List<T> retain(List<T> list) {
return remove(list, false);
}
/**
* 获取指定开始(包含)和结束(包含)下标的元素
* @param fromIndex
* @param toIndex
* @return
*/
@Override
public List<T> subList(int fromIndex, int toIndex) {
checkIndex(fromIndex, toIndex);
List<T> list = new ArrayList<>();
for (int i = fromIndex; i <= toIndex; i++) {
list.add((T) array[i]);
}
return list;
}
/**
* 对集合进行排序
* @param cmp
*/
@Override
public void sort(Comparator<T> cmp) {
SortUtil.quickSort((T[]) array, 0, size - 1, cmp);
}
/**
* 将集合转化成数组
* @return
*/
@Override
public Object[] toArray() {
Object[] rstObj = new Object[size];
arraycopy(array, 0, rstObj, 0, size);
return rstObj;
}
/**
* 清空集合
*/
@Override
public void clear() {
release(array);
array = EMPTY;
size = 0;
}
/**
* 数组扩容或缩减时操作
* @param len
*/
private void changeArray(int len) {
temp = array;
array = new Object[len];
arraycopy(temp, 0, array, 0, size);
release(temp);
temp = EMPTY;
}
/**
* 数组缩减
*/
@Override
public void trimToSize() {
if (size <= array.length * DECREASE_FACTOR) {
changeArray(size);
}
}
/**
* 数组当前实际长度
* @return
*/
@Override
public int capacity() {
return array.length;
}
/**
* 迭代器
* @return
*/
@Override
public Iterator<T> iterator() {
return new InnerIterator<>(true);
}
/**
* 实现Iterator接口,写一个ArrayList的迭代器
* @param <T>
*/
private class InnerIterator<T> implements Iterator<T> {
//是否从前向后 true
private boolean isForward;
//数组下标
private int index;
public InnerIterator(boolean isForward) {
this.isForward = isForward;
this.index = isForward ? 0 : size - 1;
}
@Override
public boolean hasNext() {
return isForward ? index < size : index >= 0;
}
@Override
public T next() {
return (T) (isForward ? array[index++] : array[index--]);
}
/**
* 删除操作
*/
@Override
public void remove() {
arraycopy(array, (isForward ? index : index + 2), array, (isForward ? index - 1 : index + 1),
(isForward ? size - index : size - (index + 2)));
array[--size] = null;
index += isForward ? -1 : 1;
}
/**
* 获取下标
* @return
*/
public int getIndex() {
return isForward ? index - 1 : index + 1;
}
}
/**
* 实现System.arraycopy本地方法
* 将一个数组元素拷贝到另一个数组
*public static native void arraycopy(Object src, int srcPos,
* Object dest, int destPos,
* int length);
* @param src 源数组
* @param srcPos 源数组下标位置
* @param dest 目标数组
* @param destPos 目标数组的下标位置
* @param length 源数组要拷贝的长度
*/
private void arraycopy(Object[] src, int srcPos, Object[] dest, int destPos, int length) {
Object[] newSrc = null;
if(src == dest) {
newSrc = new Object[size];
for (int i = 0; i < size; i++) {
newSrc[i] = src[i];
}
}
for (int i = srcPos; i < srcPos + length; i++) {
dest[destPos++] = (src == dest ? newSrc[i] : src[i]);
}
}
/**
* 释放数组指向引用,为的防止,某个对象被删除以后,还有引用指向它,虽然是垃圾
* 但是,因为有引用指向它,不能被及时回收
* @param relObj
*/
private void release(Object[] relObj) {
for (int i = 0; i < size; i++) {
relObj[i] = null;
}
}
/**
* 下标值判断
* @param index
* @param name
*/
private void checkIndex(int index, String...name) {
if (index >= size || index < 0) {
throw new IndexOutOfBoundsException((name.length > 0 ? name[0] : "index") + "下标输入错误,输入下标为:" +
index + ",当前集合下标范围为0~" + (size - 1) + "。");
}
}
/**
* 下标值判断
* @param fromIndex
* @param toIndex
*/
private void checkIndex(int fromIndex, int toIndex) {
if (toIndex < fromIndex) {
throw new RuntimeException("toIndex值为:" + toIndex + "小于fromIndex值为:" + fromIndex);
}
checkIndex(fromIndex, "fromIndex");
checkIndex(toIndex, "toIndex");
}
}
排序工具类代码,这里用的是快排
package com.wxl.practices.ArrayList;
import java.util.Comparator;
/**
* 此排序专门为List调用,无法单独调用,因为此处需要泛型数组
* 因此调用者需要是一个泛型
* 因为List本来就是一个泛型集合,因此可以调用
*/
public class SortUtil {
/**
* 排序值交换方法
* @param array 数组
* @param fromIndex 要交换值的数组下标
* @param toIndex 要交换值的数组下标
* @param <T> 泛型
*/
private static <T> void swap(T[] array, int fromIndex, int toIndex) {
T temp = array[fromIndex];
array[fromIndex] = array[toIndex];
array[toIndex] = temp;
}
/**
* 获取中位数的下标
* @param array 数组
* @param beginIndex 开始索引
* @param endIndex 结束索引
* @param cmp 比较器
* @param <T>
* @return
*/
private static <T> int getMidIndex(T[] array, int beginIndex, int endIndex, Comparator<T> cmp) {
int oldBeginIndex = beginIndex;
while (beginIndex < endIndex) {
while (beginIndex < endIndex && cmp.compare(array[oldBeginIndex], array[endIndex]) <= 0) endIndex--;
while (beginIndex < endIndex && cmp.compare(array[oldBeginIndex], array[beginIndex]) >= 0) beginIndex++;
if (beginIndex < endIndex) {
swap(array, beginIndex, endIndex);
}
}
if (oldBeginIndex != beginIndex) {
swap(array, oldBeginIndex, beginIndex);
}
return beginIndex;
}
/**
* 快速排序
* 通过递归,将一个数分成两部分,再将两部分的每部分,再分两部分...直到数组有序结束
* 相当于分治排序
* @param array 数组
* @param beginIndex 开始索引
* @param endIndex 结束索引
* @param cmp 比较器
* @param <T>
*/
public static <T> void quickSort(T[] array, int beginIndex, int endIndex, Comparator<T> cmp) {
if(beginIndex >= endIndex) return;
int midIndex = getMidIndex(array, beginIndex, endIndex, cmp);
quickSort(array, beginIndex, midIndex - 1, cmp);
quickSort(array, midIndex + 1, endIndex, cmp);
}
}