线性集合:ArrayList,LinkedList,Vector/Stack

线性集合:ArrayList,LinkedList,Vector/Stack

码农世界 2024-05-13 前端 70 次浏览 0个评论

共同点:都是线性集合

ArrayList

ArrayList 底层是基于数组实现的,并且实现了动态扩容(当需要添加新元素时,如果 elementData 数组已满,则会自动扩容,新的容量将是原来的 1.5 倍),来看一下 ArrayList 的部分源码(PS:以下代码均来自Java8,不同版本可能存在细微差距)。

public class ArrayList extends AbstractList
        implements List, RandomAccess, Cloneable, java.io.Serializable
{
    private static final long serialVersionUID = 8683452581122892189L;
    private static final int DEFAULT_CAPACITY = 10; // 默认容量
    private static final Object[] EMPTY_ELEMENTDATA = {};
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
    transient Object[] elementData; // 存储元素的数组,数组类型:Object
    private int size; // 列表的大小,即列表中元素的个数

ArrayList 还实现了 RandomAccess 接口,这是一个标记接口:

public interface RandomAccess {
}

内部是空的,标记“实现了这个接口的类支持快速(通常是固定时间)随机访问”。快速随机访问是什么意思呢?就是说不需要遍历,就可以通过下标(索引)直接访问到内存地址。而 LinkedList 没有实现该接口,表示它不支持高效的随机访问,需要通过遍历来访问元素。

ArrayList 还实现了 Cloneable 接口,并且重写了 Object 类的 clone() 方法,但只是浅拷贝,还是要根据需求使用。

public Object clone() {
    try {
        ArrayList v = (ArrayList) super.clone();
        v.elementData = Arrays.copyOf(elementData, size);
        v.modCount = 0;
        return v;
    } catch (CloneNotSupportedException e) {
        // this shouldn't happen, since we are Cloneable
        throw new InternalError(e);
    }
}

ArrayList 还实现了 Serializable 接口,支持序列化:

但是关键字段 elementData 使用了 transient 关键字修饰,这个关键字的作用是,让它修饰的字段不被序列化。

看到这里是不是心里出现了很多问好?

我们这样来看:elementData 是一个数组,数组是定长的,如果一个新创建的ArrayList,并且我们只往里添加了2个元素,如果我们默认序列化就会多序列化8个空的内存空间,我们再反序列化出来的时候需要更大的空间去接收这个数组。如下例子中可以更好的反应该问题,可能出现很大的bug:

public class Main {
    public static void main(String[] args) throws Exception {
        List list = new ArrayList<>();
        for (int i = 0; i < 100000; i++) {
            list.add(i);
        }
        System.out.println(list.size());
        list.clear();
        Class listClass = list.getClass();
        Field field = listClass.getDeclaredField("elementData");
        field.setAccessible(true);
        Object[] o = (Object[]) field.get(list);
        System.out.println(o.length);
    }
}
输出如下:
100000
106710

 于是,ArrayList 做了一个愉快而又聪明的决定,内部提供了两个私有方法 writeObject 和 readObject 来完成序列化和反序列化。

private void writeObject(java.io.ObjectOutputStream s)
        throws java.io.IOException{
    // Write out element count, and any hidden stuff
    int expectedModCount = modCount;
    s.defaultWriteObject();
    // Write out size as capacity for behavioural compatibility with clone()
    s.writeInt(size);
    // Write out all elements in the proper order.
    for (int i=0; i

转载请注明来自码农世界,本文标题:《线性集合:ArrayList,LinkedList,Vector/Stack》

百度分享代码,如果开启HTTPS请参考李洋个人博客
每一天,每一秒,你所做的决定都会改变你的人生!

发表评论

快捷回复:

评论列表 (暂无评论,70人围观)参与讨论

还没有评论,来说两句吧...

Top