死磕java集合之ArrayList源码分析(JDK18)

本文详细分析了ArrayList的源码,包括默认构造、带容量构造、从集合构造的方法,以及add、set、remove等操作。重点探讨了扩容机制,当数组空间不足时,如何按一定比例增长。此外,还介绍了addAll、removeAll、retainAll和removeIf等集合操作,以及ArrayList的序列化和反序列化过程。
摘要由CSDN通过智能技术生成

死磕java集合之ArrayList源码分析(JDK18)

简介

ArrayList是一种以数组实现的List,与数组相比,它具有动态扩展的能力,因此也可称之为动态数组。

继承体系

image-20220323002746932

ArrayList实现了List, RandomAccess, Cloneable, java.io.Serializable等接口。

ArrayList实现了List,提供了基础的添加、删除、遍历等操作。

ArrayList实现了RandomAccess,提供了随机访问的能力。

ArrayList实现了Cloneable,可以被克隆。

ArrayList实现了Serializable,可以被序列化。

源码解析

属性

/**
 * 默认容量
 */
private static final int DEFAULT_CAPACITY = 10;

/**
 * 空数组,如果传入的容量为0时使用
 */
private static final Object[] EMPTY_ELEMENTDATA = {};

/**
 * 空数组,传传入容量时使用,添加第一个元素的时候会重新初始为默认容量大小
 */
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

/**
 * 存储元素的数组
 */
transient Object[] elementData; // non-private to simplify nested class access

/**
 * 集合中元素的个数
 */
private int size;

// AbstractList中的属性
// 这个列表在结构上被修改的次数。结构修改是指改变列表的大小,或者以一种可能产生不正确结果的方式扰乱列表。
protected transient int modCount = 0;

(1)DEFAULT_CAPACITY

默认容量为10,也就是通过new ArrayList()创建时的默认容量。

(2)EMPTY_ELEMENTDATA

空的数组,这种是通过new ArrayList(0)创建时用的是这个空数组。

(3)DEFAULTCAPACITY_EMPTY_ELEMENTDATA

也是空数组,这种是通过new ArrayList()创建时用的是这个空数组,与EMPTY_ELEMENTDATA的区别是在添加第一个元素时使用这个空数组的会初始化为DEFAULT_CAPACITY(10)个元素。

(4)elementData

真正存放元素的地方,使用transient是为了不序列化这个字段。

至于没有使用private修饰,后面注释是写的“为了简化嵌套类的访问”,但是楼主实测加了private嵌套类一样可以访问。

private表示是类私有的属性,只要是在这个类内部都可以访问,嵌套类或者内部类也是在类的内部,所以也可以访问类的私有成员。

(5)size

真正存储元素的个数,而不是elementData数组的长度。

ArrayList(int initialCapacity)构造方法

传入初始容量,如果大于0就初始化elementData为对应大小,如果等于0就使用EMPTY_ELEMENTDATA空数组,如果小于0抛出异常。

public ArrayList(int initialCapacity) {
    if (initialCapacity > 0) {
        // 如果传入的初始容量大于0,就新建一个数组存储元素
        this.elementData = new Object[initialCapacity];
    } else if (initialCapacity == 0) {
        // 如果传入的初始容量等于0,使用空数组EMPTY_ELEMENTDATA
        this.elementData = EMPTY_ELEMENTDATA;
    } else {
        // 如果传入的初始容量小于0,抛出异常
        throw new IllegalArgumentException("Illegal Capacity: " + initialCapacity);
    }
}

ArrayList()构造方法

不传初始容量,初始化为DEFAULTCAPACITY_EMPTY_ELEMENTDATA空数组,会在添加第一个元素的时候扩容为默认的大小,即10。

public ArrayList() {
    // 如果没有传入初始容量,则使用空数组DEFAULTCAPACITY_EMPTY_ELEMENTDATA
    // 使用这个数组是在添加第一个元素的时候会扩容到默认大小10
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

ArrayList(Collection<? extends E> c)构造方法

传入集合并初始化elementData,这里会使用拷贝把传入集合的元素拷贝到elementData数组中,如果元素个数为0,则初始化为EMPTY_ELEMENTDATA空数组。

/**
* 把传入集合的元素初始化到ArrayList中
*/
public ArrayList(Collection<? extends E> c) {
    	// 集合转数组
        Object[] a = c.toArray();
        if ((size = a.length) != 0) {
            // 如果c的类型为ArrayList
            if (c.getClass() == ArrayList.class) {
                elementData = a;
            } else {
                // 如果不是,拷贝成Object[].class类型,并赋值给elementData
                elementData = Arrays.copyOf(a, size, Object[].class);
            }
        } else {
            // 如果c的空集合,则初始化为空数组EMPTY_ELEMENTDATA
            elementData = EMPTY_ELEMENTDATA;
        }
}

为什么c.toArray();返回的有可能不是Object[]类型呢?请看下面的代码:

public class ArrayTest {
    public static void main(String[] args) {
        Father[] fathers = new Son[]{};
        // 打印结果为class [Lcom.coolcoding.code.Son;
        System.out.println(fathers.getClass());

        List<String> strList = new MyList();
        // 打印结果为class [Ljava.lang.String;
        System.out.println(strList.toArray().getClass());
    }
}

class Father {}

class Son extends Father {}

class MyList extends ArrayList<String> {
    /**
     * 子类重写父类的方法,返回值可以不一样
     * 但这里只能用数组类型,换成Object就不行
     * 应该算是java本身的bug
     */
    @Override
    public String[] toArray() {
        // 为了方便举例直接写死
        return new String[]{"1", "2", "3"};
    }
}

add(E e)方法

添加元素到末尾,平均时间复杂度为O(1)。

public boolean add(E e) {
    // ArrayList结构有变化,modCount加1
    modCount++;
    // 将e加入elementData中
    add(e, elementData, size);
    return true;
}

add(E e, Object[] elementData, int s)方法

private void add(E e, Object[] elementData, int s) {
    // 如果当前elementData数组中真实的元素个数s(size)已达到elementData的长度,则需要扩容
    if (s == elementData.length)
        // 扩容
        elementData = grow();
    // 在最后一个元素上,加入e
    elementData[s] = e;
    // size加1
    size = s + 1;
}

private Object[] grow() {
        return grow(size + 1);
}

private Object[] grow(int minCapacity) {
    	// 旧容量
        int oldCapacity = elementData.length;
    	// 如果旧容量大于0,或elementData数组不为空数组
        if (oldCapacity > 0 || elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            // 这里最小growth为minCapacity - oldCapacity,最佳growth为旧容量的一半
            int newCapacity = ArraysSupport.newLength(oldCapacity,
                    minCapacity - oldCapacity, /* minimum growth */
                    oldCapacity >> 1           /* preferred growth */);
            // 复制element数组到新数组,并使新数组长度为newCapacity,并赋值给elementData
            return elementData = Arrays.copyOf(elementData, newCapacity);
        } else {
            // 如果elementData为空数组,数组长度取默认长度10和minCapacity的最大值
            return elementData = new Object[Math.max(DEFAULT_CAPACITY, minCapacity)];
        }
}

// 这个函数在类ArraysSupport中
// 在给定数组的当前长度、最小增长量和首选增长量的情况下,计算新的数组长度
public static int newLength(int oldLength, int minGrowth, int prefGrowth) {
        // preconditions not checked because of inlining
        // assert oldLength >= 0
        // assert minGrowth > 0
    
		// 得到首选长度
        int prefLength = oldLength + Math.max(minGrowth, prefGrowth); // might overflow
        if (0 < prefLength && prefLength <= SOFT_MAX_ARRAY_LENGTH) {
            // 如果首选长度大于0小于等于软最大阵列长度(Integer.MAX_VALUE - 8)
            // 由阵列增长计算施加的软最大阵列长度。实际的限制可能取决于一些JVM实现特定的特征,比如对象头的大小。软最大值的选择是保守的,以便小于可能遇到的任何实现限制
            return prefLength;
        } else {
            // put code cold in a separate method
            // 计算得到一个大的长度值
            return hugeLength(oldLength, minGrowth);
        }
}

// 这个函数在类ArraySupport中
private static int hugeLength(int oldLength, int minGrowth) {
        int minLength = oldLength + minGrowth;
        if (minLength < 0) { // overflow
            // 如果minLength超过了Integer的范围
            throw new OutOfMemoryError(
                "Required array length " + oldLength + " + " + minGrowth + " is too large");
        } else if (minLength <= SOFT_MAX_ARRAY_LENGTH) {
            // 如果小于等于SOFT_MAX_ARRAY_LENGTH
            return SOFT_MAX_ARRAY_LENGTH;
        } else {
            // 如果大于SOFT_MAX_ARRAY_LENGTH,小于等于Integer.MAX_VALUE
            return minLength;
        }
}

// 复制指定的数组,用空值截断或填充(如果需要),使复制具有指定的长度
public static <T> T[] copyOf(T[] original, int newLength) {
        return (T[]) copyOf(original, newLength, original.getClass());
}

// 复制指定的数组,用空值截断或填充(如果需要),使复制具有指定的长度
public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
    	// 按照newType的class类型创建一个长度为newLength的数组
        @SuppressWarnings("unchecked")
        T[] copy = ((Object)newType == (Object)Object[].class)
            ? (T[]) new Object[newLength]
            : (T[]) Array.newInstance(newType.getComponentType(), newLength);
        // 将数据从original复制到copy中
        System.arraycopy(original, 0, copy, 0,
                         Math.min(original.length, newLength));
        return copy;
}
  1. modCount加1;
  2. 检查是否需要扩容;
    1. 需要扩容时,正常扩容至原来的1.5倍,特殊的扩容至SOFT_MAX_ARRAY_LENGTH或(SOFT_MAX_ARRAY_LENGTH, Integer.MAX_VALUE]
  3. 将e加入elementData的索引size位;
  4. size加1。

add(int index, E element)方法

添加元素到指定位置,平均时间复杂度为O(n)。

public void add(int index, E element) {
    	// 检查是否越界
        rangeCheckForAdd(index);
    	// modCount加1
        modCount++;
        final int s;
        Object[] elementData;
    	// 如果需要扩容
        if ((s = size) == (elementData = this.elementData).length)
            // 扩容
            elementData = grow();
    	// 将inex及其之后的元素往后挪一位,则index位置处就空出来了
        System.arraycopy(elementData, index,
                         elementData, index + 1,
                         s - index);
    	// 将元素插入到index的位置
        elementData[index] = element;
    	// size加1
        size = s + 1;
}
  1. 检查索引是否越界;
  2. modCount加1;
  3. 检查是否需要扩容
    1. 需要需要扩容时,正常扩容至原来的1.5倍,特殊的扩容至SOFT_MAX_ARRAY_LENGTH或(SOFT_MAX_ARRAY_LENGTH, Integer.MAX_VALUE]
  4. 在索引index处插入元素element;
  5. size加1。

addAll(Collection<? extends E> c)方法

求两个集合的并集,如果原ArrayList有变化,返回true,无变化返回false

public boolean addAll(Collection<? extends E> c) {
    	// 集合转数组
        Object[] a = c.toArray();
    	// modCount加1
        modCount++;
        int numNew = a.length;
    	// 如果集合为空,返回false
        if (numNew == 0)
            return false;
        Object[] elementData;
        final int s;
    	// 如果集合的大小比elementData当前空闲的长度大时,需要扩容
        if (numNew > (elementData = this.elementData).length - (s = size))
            // 扩容(将容量扩充到原始容量的1.5倍或s+numNew大小
            elementData = grow(s + numNew);
    	// 把数组a中的元素拷贝到elementData的尾部
        System.arraycopy(a, 0, elementData, s, numNew);
    	// 更新size
        size = s + numNew;
        return true;
}

addAll(int index, Collection<? extends E> c)方法

在指定索引位置,加入集合c求并集,如果原ArrayList有变化,返回true,无变化返回false

public boolean addAll(int index, Collection<? extends E> c) {
    // 检查索引是否在[0,size]范围内
    rangeCheckForAdd(index);
	// 集合c转数组
    Object[] a = c.toArray();
    // modCount加1
    modCount++;
    int numNew = a.length;
    // 如果集合为空,返回false
    if (numNew == 0)
        return false;
    Object[] elementData;
    final int s;
    // 如果集合的大小比elementData当前空闲的长度大时,需要扩容
    if (numNew > (elementData = this.elementData).length - (s = size))
        elementData = grow(s + numNew);
	// 得到需要移动的数组部分的长度
    int numMoved = s - index;
    if (numMoved > 0)
        // 如果索引不在size时
        // 移动原数组的[index, size -1]到[index + numNew, index + numNew + numMoved - 1]
        System.arraycopy(elementData, index,
                         elementData, index + numNew,
                         numMoved);
    System.arraycopy(a, 0, elementData, index, numNew);
    size = s + numNew;
    return true;
}

get(int index)方法

获取指定索引位置的元素,时间复杂度为O(1)

public E get(int index) {
    // 检查是否越界,看索引index是否在[0,size)内
    Objects.checkIndex(index, size);
    // 返回数组index位置的元素
    return elementData(index);
}

// 在Object类中
@ForceInline
public static int checkIndex(int index, int length) {
        return Preconditions.checkIndex(index, length, null);
}

// 在Preconditions类中
@IntrinsicCandidate
public static <X extends RuntimeException> int checkIndex(int index, int length,
                   BiFunction<String, List<Number>, X> oobef) {
        if (index < 0 || index >= length)
            // 这里oobef为null
            throw outOfBoundsCheckIndex(oobef, index, length);
        return index;
}

// 在Preconditions类中
private static RuntimeException outOfBoundsCheckIndex(
            BiFunction<String, List<Number>, ? extends RuntimeException> oobe,
            int index, int length) {
    	// 这里oobe为null
        return outOfBounds(oobe, "checkIndex", index, length);
}

private static RuntimeException outOfBounds(
            BiFunction<String, List<Number>, ? extends RuntimeException> oobef,
            String checkKind,
            Number... args) {
        List<Number> largs = List.of(args);
    	// 这里oobef为null
        RuntimeException e = oobef == null
                             ? null : oobef.apply(checkKind, largs);
        // 这里e为null,checkKind为"checkIndex"
    	return e == null
               ? new IndexOutOfBoundsException(outOfBoundsMessage(checkKind, largs)) : e;
}

// 处理抛出的异常的异常内容,这里checkKind为"checkIndex"
private static String outOfBoundsMessage(String checkKind, List<? extends Number> args) {
        if (checkKind == null && args == null) {
            return String.format("Range check failed");
        } else if (checkKind == null) {
            return String.format("Range check failed: %s", args);
        } else if (args == null) {
            return String.format("Range check failed: %s", checkKind);
        }

        int argSize = 0;
        switch (checkKind) {
            case "checkIndex":
                // 走到这里
                argSize = 2;
                break;
            case "checkFromToIndex":
            case "checkFromIndexSize":
                argSize = 3;
                break;
            default:
        }

        // Switch to default if fewer or more arguments than required are supplied
        switch ((args.size() != argSize) ? "" : checkKind) {
            case "checkIndex":
                // 走这里
                return String.format("Index %s out of bounds for length %s",
                                     args.get(0), args.get(1));
            case "checkFromToIndex":
                return String.format("Range [%s, %s) out of bounds for length %s",
                                     args.get(0), args.get(1), args.get(2));
            case "checkFromIndexSize":
                return String.format("Range [%s, %<s + %s) out of bounds for length %s",
                                     args.get(0), args.get(1), args.get(2));
            default:
                return String.format("Range check failed: %s %s", checkKind, args);
        }
}

E elementData(int index) {
        return (E) elementData[index];
}
  1. 检查索引是否越界,越界抛出IndexOutOfBoundsException异常(异常内容为“Index index out of bounds for length size”);
  2. 返回索引位置的元素。

set(int index, E element)方法

在索引index处设置一个新值,并返回旧值

public E set(int index, E element) {
    // 检查是否越界,看索引index是否在[0,size)内
    Objects.checkIndex(index, size);
    // 获取索引处数组的旧值
    E oldValue = elementData(index);
    // 设定新值
    elementData[index] = element;
    // 返回旧值
    return oldValue;
}

remove(int index)方法

删除指定索引位置的元素,时间复杂度为O(n)。

public E remove(int index) {
    // 检查索引是否越界
    Objects.checkIndex(index, size);
    // 使用es来作为变量,引用elementData,这里是引用传递,后面es的变动都会反映到elementData
    final Object[] es = elementData;

    // 得到旧值
    @SuppressWarnings("unchecked") E oldValue = (E) es[index];
    // 删除旧值
    fastRemove(es, index);

    // 返回旧值
    return oldValue;
}

private void fastRemove(Object[] es, int i) {
    	// modCount加1
        modCount++;
        final int newSize;
    	// 如果删除的不是最后一位
        if ((newSize = size - 1) > i)
            // 索引i后面的元素往前移一位
            System.arraycopy(es, i + 1, es, i, newSize - i);
    	// size减1,并将索引size处的值置为null,方便GC
        es[size = newSize] = null;
}
  1. 检查索引是否越界;
  2. 得到旧值;
  3. 快速删除;
    1. modCount加1;
    2. 如果删除的不是最后一位,则索引i后面的元素都往前移1位
    3. size减1,并将索引size处的值置为null,方便GC
  4. 返回旧值。

remove(Object o)方法

删除指定元素值的元素,时间复杂度为O(n)。

public boolean remove(Object o) {
        final Object[] es = elementData;
        final int size = this.size;
        int i = 0;
    	// 找到es中第一次出现o的索引i,找不到则返回false
        found: {
            if (o == null) {
                for (; i < size; i++)
                    // 如果要删除的元素为null,则以null进行比较,使用==
                    if (es[i] == null)
                        break found;
            } else {
                for (; i < size; i++)
                    // 如果要删除的元素不为null,则进行比较,使用equals()方法
                    if (o.equals(es[i]))
                        break found;
            }
            return false;
        }
    	// 找到符合条件的索引i,快速删除
        fastRemove(es, i);
        return true;
}
  1. 找到第一个等于指定元素值的元素,如果找不到返回false;
  2. 找到了,快速删除,并返回true。

注意:

当传入的参数是整数时,需要区分是基础数据类型int,还是包装类Integer,如果是int则采用remove(int index),如果是Integer则采用remove(Object o)。

retainAll(Collection<?> c)方法

求两个集合的交集。

public boolean retainAll(Collection<?> c) {
        return batchRemove(c, true, 0, size);
}
// 按条件批量删除ArrayList中[from,end)的元素
// complement为true,删除ArrayList中所有不包含在集合c中的元素,得到的是ArrayList与集合c的交集
// complement为false,删除ArrayList中所有包含在集合c中的元素,得到的是ArrayList与集合c的差集
boolean batchRemove(Collection<?> c, boolean complement,
                        final int from, final int end) {
    	// 要求集合c不能为空
        Objects.requireNonNull(c);
        final Object[] es = elementData;
        int r;
        // Optimize for initial run of survivors
        for (r = from;; r++) {
            if (r == end)
                // complement为true,解释为:ArrayList中的元素全在集合c中,返回false,表示未删除元素
                // complement为false,解释为:没有交集,ArrayList中的所有元素都不在集合c中,返回false,表示未删除元素
                return false;
            if (c.contains(es[r]) != complement)
                // complement为true,解释为在ArrayList中找到第一个不在集合c中的元素的索引r
                // completion为false,解释为在ArrayList中找到第一个在集合c中的元素的索引r
                break;
        }
    	// w指向找到的索引r, r取下一位r+1
        int w = r++;
        try {
            for (Object e; r < end; r++)
                if (c.contains(e = es[r]) == complement)
                    // complement为true,解释为:在ArrayList中找到一个在集合c中的元素的,并将这个元素写入es[w],并将w加1
                 	// complement为false,解释为:在ArrayList中找到一个元素不在集合c中的元素,并将这个元素写入es[w],并将w加1   
                    es[w++] = e;
        } catch (Throwable ex) {
            // Preserve behavioral compatibility with AbstractCollection,
            // even if c.contains() throws.
            // 保持行为与AbstractCollection兼容
            // 将[r,end -1]的元素写到[w, end - r + w - 1]中
            System.arraycopy(es, r, es, w, end - r);
            // 此时w应向后移end - r
            w += end - r;
            throw ex;
        } finally {
            // 变动的元素为end-w个
            modCount += end - w;
            // 擦除[w, end)之间的元素,end等于size时,不需要移动元素
            shiftTailOverGap(es, w, end);
        }
    	// 有删除元素,返回true
        return true;
}

// 在Object类中
public static <T> T requireNonNull(T obj) {
        if (obj == null)
            throw new NullPointerException();
        return obj;
}

// 找到的索引大于等于0,说明集合包含这个元素o
public boolean contains(Object o) {
        return indexOf(o) >= 0;
}

public int indexOf(Object o) {
        return indexOfRange(o, 0, size);
}

// 在[start, end)这个范围内查找o的索引
int indexOfRange(Object o, int start, int end) {
        Object[] es = elementData;
        if (o == null) {
            for (int i = start; i < end; i++) {
                // 如果要删除的元素为null,则以null进行比较,使用==
                if (es[i] == null) {
                    return i;
                }
            }
        } else {
            for (int i = start; i < end; i++) {
                // 如果要删除的元素不为null,则进行比较,使用equals()方法
                if (o.equals(es[i])) {
                    return i;
                }
            }
        }
        return -1;
}

// 擦除[lo, hi)之间的元素,并将[hi, size-1]的元素前移 hi - lo 位
private void shiftTailOverGap(Object[] es, int lo, int hi) {
    	// 将[hi,size - 1]的元素复制到[lo, size - 1 - (hi - lo)]
        System.arraycopy(es, hi, es, lo, size - hi);
    	// 擦除[size - (hi - lo), size - 1]的元素
        for (int to = size, i = (size -= hi - lo); i < to; i++)
            es[i] = null;
}
  1. 遍历elementData数组;
  2. 如果元素在c中,则把这个元素添加到elementData数组的w位置并将w位置往后移一位;
  3. 遍历完之后,w之前的元素都是两者共有的,w之后(包含)的元素不是两者共有的;
  4. 将w以后(包含)的元素置为null,方便GC回收。

removeAll(Collection<?> c)

求两个集合的单方向差集,只保留当前集合中不在c中的元素,不保留在c中不在当前集体中的元素。

public boolean removeAll(Collection<?> c) {
    return batchRemove(c, false, 0, size);
}

与retainAll(Collection<?> c)方法类似,只是这里保留的是不在c中的元素。

removeIf(Predicate<? super E> filter)

从ArrayList中删除满足给定谓词filter的所有元素

@Override
public boolean removeIf(Predicate<? super E> filter) {
    return removeIf(filter, 0, size);
}

// 删除满足给定谓词的所有元素,从索引i(包含)到索引end(排除)。如果存在删除行为,返回true;无删除行为,返回false
boolean removeIf(Predicate<? super E> filter, int i, final int end) {
    	// 检查filter是否为null
        Objects.requireNonNull(filter);
        int expectedModCount = modCount;
        final Object[] es = elementData;
        // Optimize for initial run of survivors
    	// 找到第一个满足给定谓词filter的元素的索引i
        for (; i < end && !filter.test(elementAt(es, i)); i++)
            ;
        // Tolerate predicates that reentrantly access the collection for
        // read (but writers still get CME), so traverse once to find
        // elements to delete, a second pass to physically expunge.
        if (i < end) {
            // 开始遍历
            final int beg = i;
            final long[] deathRow = nBits(end - beg);
            // 将i记录在deathRow[0]
            deathRow[0] = 1L;   // set bit 0
            for (i = beg + 1; i < end; i++)
                // 如果满足函数式断言
                if (filter.test(elementAt(es, i)))
                    // 记录需要删除元素的下标
                    setBit(deathRow, i - beg);
            if (modCount != expectedModCount)
                // 标记过程出现多线程并发修改,则抛出 ConcurrentModificationException
                throw new ConcurrentModificationException();
            modCount++;
            int w = beg;
            for (i = beg; i < end; i++)
                // 从第一个被删除的位置开始,将需要保留的元素顺序进行填充
                if (isClear(deathRow, i - beg))
                    es[w++] = es[i];
            // 擦除[w, end)之间的元素
            shiftTailOverGap(es, w, end);
            return true;
        } else {
            // 没找到满足给定谓词的索引
            if (modCount != expectedModCount)
                // 处理过程中发生结构型变化
                throw new ConcurrentModificationException();
            return false;
        }
 }

// 取得es数组上指定索引的元素
static <E> E elementAt(Object[] es, int index) {
        return (E) es[index];
}

private static long[] nBits(int n) {
        return new long[((n - 1) >> 6) + 1];
}

private static void setBit(long[] bits, int i) {
        bits[i >> 6] |= 1L << i;
}

private static boolean isClear(long[] bits, int i) {
        return (bits[i >> 6] & (1L << i)) == 0;
}

总结

  1. ArrayList内部使用数组存储元素,当数组长度不够时进行扩容,每次加一半的空间,ArrayList不会进行缩容;
  2. ArrayList支持随机访问,通过索引访问元素极快,时间复杂度为O(1);
  3. ArrayList添加元素到尾部极快,平均时间复杂度为O(1);
  4. ArrayList添加元素到中间比较慢,因为要搬移元素,平均时间复杂度为O(n);
  5. ArrayList从尾部删除元素极快,时间复杂度为O(1);
  6. ArrayList从中间删除元素比较慢,因为要搬移元素,平均时间复杂度为O(n);
  7. ArrayList支持求并集,调用addAll(Collection<? extends E> c)方法即可;
  8. ArrayList支持求交集,调用retainAll(Collection<? extends E> c)方法即可;
  9. ArrayList支持求单向差集,调用removeAll(Collection<? extends E> c)方法即可;

彩蛋

@java.io.Serial
private void writeObject(java.io.ObjectOutputStream s)
    throws java.io.IOException {
    // Write out element count, and any hidden stuff
    // 防止序列化期间有修改
    int expectedModCount = modCount;
    // 写出非transient非static属性(会写出size属性)
    s.defaultWriteObject();

    // Write out size as capacity for behavioral compatibility with clone()
    // 写出元素个数
    s.writeInt(size);

    // Write out all elements in the proper order.
    // 依次写出元素
    for (int i=0; i<size; i++) {
        s.writeObject(elementData[i]);
    }

    // 如果有修改,抛出异常
    if (modCount != expectedModCount) {
        throw new ConcurrentModificationException();
    }
}

@java.io.Serial
private void readObject(java.io.ObjectInputStream s)
    throws java.io.IOException, ClassNotFoundException {

    // Read in size, and any hidden stuff
    // 读入非transient非static属性(会读取size属性)
    s.defaultReadObject();

    // Read in capacity
    // 读入元素个数,没什么用,只是因为写出的时候写了size属性,读的时候也要按顺序来读
    s.readInt(); // ignored

    if (size > 0) {
        // like clone(), allocate array based upon size not capacity
        // 就像克隆,分配数组的长度是基于size而不是capacity(即有多少,用多少)
        SharedSecrets.getJavaObjectInputStreamAccess().checkArray(s, Object[].class, size);
        Object[] elements = new Object[size];

        // Read in all elements in the proper order.
        for (int i = 0; i < size; i++) {
            elements[i] = s.readObject();
        }

        elementData = elements;
    } else if (size == 0) {
        // size等于0,elementData赋值{}
        elementData = EMPTY_ELEMENTDATA;
    } else {
        // size小于0,抛出异常
        throw new java.io.InvalidObjectException("Invalid size: " + size);
    }
}

查看writeObject()方法可知,先调用s.defaultWriteObject()方法,再把size写入到流中,再把元素一个一个的写入到流中。

一般地,只要实现了Serializable接口即可自动序列化,writeObject()和readObject()是为了自己控制序列化的方式,这两个方法必须声明为private,在java.io.ObjectStreamClass#getPrivateMethod()方法中通过反射获取到writeObject()这个方法。

在ArrayList的writeObject()方法中先调用了s.defaultWriteObject()方法,这个方法是写入非static非transient的属性,在ArrayList中也就是size属性。同样地,在readObject()方法中先调用了s.defaultReadObject()方法解析出了size属性。

elementData定义为transient的优势,自己根据size序列化真实的元素,而不是根据数组的长度序列化元素,减少了空间占用。

本文是参照彤哥的文章写的,故文末附上彤哥的文章链接,可点击链接查看彤哥的更多文章。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值