关于编辑器:ArrayList是List接口的实现类

11次阅读

共计 4614 个字符,预计需要花费 12 分钟才能阅读完成。

ArrayList 汇合特点及源码剖析
ArrayList 是 List 接口的实现类

public class ArrayList<E> extends AbstractList<E>

    implements List<E>, RandomAccess, Cloneable, java.io.Serializable

继承了 AbstractList 类,实现了 Cloneable 克隆接口、Serializable 序列化接口、RandomAccess 随机拜访接口、List 接口

特点:底层应用数组实现的 查问效率高,增删慢,线程不平安、容许为空、可反复

public static void main(String[] args) {

List<String> list=new ArrayList<String>();
boolean bool=list.add("ylc");//Collection 接口增加元素
list.add(1,"ww");// 依据索引增加元素
String el=list.get(1);// 依据索引获取元素
list.size();// 获取元素个数
list.remove(2);// 依据元素地位删除
list.remove("ylc");// 删除指定元素
list.set(1,"yy");// 替换元素
list.clear();// 清空集合
list.isEmpty();// 判断元素是否为空
list.contains("ylc");// 判断汇合是否蕴含某个元素
list.indexOf("ylc");// 查找所诉中所在的地位
list.lastIndexOf("ylc");// 元素最初呈现的索引地位
Object[] objects=list.toArray();// 把汇合转化为 object 数组
for (int i = 0; i < objects.length; i++) {String str=(String) objects[i];
    System.out.println(str);
}
String[] strings=list.toArray(new String[list.size()]);// 把汇合转化为指定类型数组
List<String> list2=new ArrayList<String>();
list2.add("s");
list.addAll(list2);// 汇合合并操作
list.retainAll(list2);// 汇合交加操作 list 存储交加内容
list.removeAll(list2);// 删除 list 中含有 list2 汇合的元素 

}
ArrayList 源码剖析 #
成员变量 #
private static final int DEFAULT_CAPACITY = 10;// 数组默认长度
private static final Object[] EMPTY_ELEMENTDATA = {};// 给定一个空数组
transient Object[] elementData;// 存储 ArrayList 元素的长期数组 不会被存到磁盘
private int size;// 记录数组中元素的个数
protected transient int modCount = 0; // 汇合数组批改次数的标识(fail-fast 机制)
transient 关键字对于不想进⾏序列化的字段,使⽤ transient 关键字润饰

JDK7 中,只有创立 ArrayList 数组,就会默认创立一个长度为 10 的空数组。

JDK8 中,做了一个提早加载,在创立 ArrayList 数组时,创立一个长度为 0 的空数组,只有在用到这数组才会对长度进行扭转, 做了一个提早加载

构造函数 #
外面蕴含三种构造函数

1. 无参构造函数

初始化数组时默认赋值一个空数组

// 无参构造函数
public ArrayList() {

    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;// 默认为 0
}

image-20210624173750562

2.int 类型的构造函数

如果传入数值大于 0 就创立指定容量大小的数组,数值为 0 为空对象数组,否则抛出异样

// 带容量大小的构造函数

public ArrayList(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);
    }
}

3. 汇合类型构造函数

把传入的汇合变成数组,赋值给 elementData

汇合不为空,传入数组类型转为 object 的话赋值给 elementData

否则就间接复制到 elementData 中

public ArrayList(Collection<? extends E> c) {Object[] a = c.toArray();// 变成数组
    if ((size = a.length) != 0) {// 汇合不为空的话
        if (c.getClass() == ArrayList.class) {// 判断与 ArrayList 类型是否统一
            elementData = a;// 赋值
        } else {elementData = Arrays.copyOf(a, size, Object[].class);// 否则间接复制到数组中
        }
    } else {elementData = EMPTY_ELEMENTDATA;// 设置为空数组}
}

减少办法 #
add(E e) 办法 #
public boolean add(E e) {

    ensureCapacityInternal(size + 1); // 当 size= 0 时
    elementData[size++] = e;//Size 还是为 0,给为 0 的 size 赋值
    return true;
}

// 确保汇合数组外部容量
private void ensureCapacityInternal(int minCapacity) {//1

    ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));// 返回 10
}

// 确保显式容量
private void ensureExplicitCapacity(int minCapacity) {//minCapacity=10

    modCount++;
    if (minCapacity - elementData.length > 0)// 10-0>0 判断扩容要害代码 当第 11 个数组退出时执行 grow 代码
        grow(minCapacity);//10
}

private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE – 8;

// 计算数组容量
private static int calculateCapacity(Object[] elementData, int minCapacity) {

    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {//0=0
        return Math.max(DEFAULT_CAPACITY, minCapacity);// DEFAULT_CAPACITY=10,minCapacity=1  返回最大值作为数组容量
    }
    return minCapacity;
}

// 扩容办法
private void grow(int minCapacity) {

    int oldCapacity = elementData.length;//0
    int newCapacity = oldCapacity + (oldCapacity >> 1); // 数组长度加上位移运算(数组长度的一半)每次扩容减少 1.5 倍  0=0+0
    if (newCapacity - minCapacity < 0)//0-10<0
        newCapacity = minCapacity; //newCapacity=0
    if (newCapacity - MAX_ARRAY_SIZE > 0)//10-MAX_ARRAY_SIZE<0
        newCapacity = hugeCapacity(minCapacity);
    elementData = Arrays.copyOf(elementData, newCapacity);// 复制操作 把一个长度为 10 复制到空的数组中,生成了一个新的长度为 10 的空数组
}

Size 默认是 0,add 办法给 Size 加上 1,并传入 ensureCapacityInternal 办法,其中 ensureCapacityInternal 办法中又调用了 ensureExplicitCapacity 办法,并传入了两个参数,第一个是 elementData 代表一个为空的数组,第二个是数组个数。在 calculateCapacity 办法中,有个 if 判断如果数组大小等于默认大小,就返回其中最大的数值,默认数组大小为 10 的话,DEFAULT_CAPACITY 也等于 10,所以返回的是 10。把 10 传入 ensureExplicitCapacity 办法中,再把 10 传入 grow 办法中,生成了一个新的长度为 10 的空数组。ensureCapacityInternal 办法执行结束。add 办法 Size 仍然为 0,给为下标为 0 索引赋值,新增一个胜利。

扩容模仿

当增加第 11 个元素时,add 办法 Size=10+1=11,传入 ensureCapacityInternal 办法,进入 calculateCapacity 办法,这时 elementData=10,而 DEFAULTCAPACITY_EMPTY_ELEMENTDATA=0,不满足间接返回 minCapacity=11,值 11 进入 ensureExplicitCapacity 办法外部,满足 11-10>0 进 grow 办法,grow 办法赋值 oldCapacity=10,newCapacity=10+10 的位移一位操作 =10+5=15,最初应用 copyOf 办法,把原来 10 大小的数组扩容到 15。

add(int index, E element) 办法 #
public void add(int index, E element) {

rangeCheckForAdd(index);
ensureCapacityInternal(size + 1); // 汇合减少容量
//elementData:原数组  index:从原数组这个索引开始复制  elementData:指标数组   index + 1:指标数组的第一个元素  size - index:复制 size-index 个元素
System.arraycopy(elementData, index, elementData, index + 1,
                 size - index);
elementData[index] = element;// 把以后所有数组替换
size++;// 索引减少 

}
// 判断索引是否越界
private void rangeCheckForAdd(int index) {

    if (index > size || index < 0)
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

这样每减少一个元素就要把以后索引复制到该元素的前面,开销很大

正文完
 0