Java ArrayList 源码分析与提高性能替代方案

探讨了ArrayList在不同场景下的性能表现,并对比了多种Java集合类,如LinkedList等,以找到适合缓冲区应用的最佳实践。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

自娱自乐,不喜莫喷。目前还是大三狗,错误很多,望大神指正。微笑

    看同学java一用到list全部是ArrayList,但是ArrayList真的万能吗?顾名思义Array==数组,添加,删除肯定要有扩张,收缩操作,会拖慢性能,对于常读的自然没关系,但是有时做信息缓冲区时,添加、删除频繁,ArrayList肯定不好。


先看结果:

不带index的添加:新建ArrayList时数组大小为0;第一次添加时新建一个大小为10的数组,以后不够用时,每次扩大一半,即:10,15,22。。。每逢这些大小,就重建数组然后把源数据拷过来,这是非常不符合缓冲区的要求的。

带index的添加:数组空间不够时即:数组大小为10,15,22。。。时先做一次扩张操作,然后再拷贝一次以完成在目标位置添加,拷贝操作最少一次,最多两次。。。喵喵喵???


java.util.concurrent下有许多集合类,你们可以自己看看,我找到的比较好用的:

ArrayList:数组实现,随机查找,随机读操作非常快,写。。。

LinkedList:链表实现,可以快速增删,顺序读取,缺点时随机读取慢,得遍历一遍,单线程时做缓冲区最适合不过

HashSet:没有重复的无序集合(插入重复元素不会理你)

TreeSet:有序集合(红黑树实现)

HashMap:最常用的map类: transient Node<K,V>[] table;  也是数组实现

linkedHashMap:顾名思义,HashMap的链表实现

weakHashMap:不会增加对象的引用计数,所以gc回收时不会考虑它里面的引用

多线程:java.util.concurrent包下有许多多线程支持的工具,不需要自己加锁来控制哦,自带锁机制:

ConcurrentLinkedQueue:多线程安全的 无边界 非阻塞 队列,链表实现,多线程缓冲区首选

ConcurrentSkipListMap:多线程安全的散列表


下面是ArrayList源码分析:

我们最常用的无参构造器:

public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

可以看出真实数据elementData只指向一个已经定义好的是一个空数组,第一次添加时肯定要重新生成array;

最常用的add第一步调用ensureCapacityInternal(size + 1),确保数组有足够的空间:

public boolean add(E e) {
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    elementData[size++] = e;
    return true;
}

我们看看ensureCapacityInternal函数:

private void ensureCapacityInternal(int minCapacity) {
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
    }

    ensureExplicitCapacity(minCapacity);
}

可以看出正如我们之前所想如果elementData是默认的话,就新建一个数组,数组大小为默认大小和所需扩张后大小之间去最大值

默认大小为:

private static final int DEFAULT_CAPACITY = 10;
即10,所以无参ArrayList构造时数组大小为0,第一次添加扩张为10

然后看扩张函数:

private void ensureExplicitCapacity(int minCapacity) {
    modCount++;

    // overflow-conscious code
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}

protected transient int modCount = 0;

transient声明的实例变量,当对象存储时,它的值不需要维持。换句话来说就是,用transient关键字标记的成员变量不参与序列化过程。

modcount只是用来记录数组变化次数的,当它的数据出错时会抛出错误,不过我们不关注,重点是grow函数:

private void grow(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    // minCapacity is usually close to size, so this is a win:
    elementData = Arrays.copyOf(elementData, newCapacity);
}
好,这就是我们要找的扩张函数最终实现了:

oldCapacity >> 1 :基本等于oldCapacity/2 取整数部分

所以每次扩张时大小为原来的3/2,即增大一半;

如果满足需求就这样定了,如果不满足,就按需求来;


最后ArrayList最大约束:如果 小,就把原来的数组烤进新数组,最大约束为:

private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
即:int的最大值减8

如果大的话;处理:

private static int hugeCapacity(int minCapacity) {
    if (minCapacity < 0) // overflow
        throw new OutOfMemoryError();
    return (minCapacity > MAX_ARRAY_SIZE) ?
        Integer.MAX_VALUE :
        MAX_ARRAY_SIZE;
}
哇,欺骗我感情,我还以为会用两个数组之类的重实现,结果就再加8位???喵喵喵???


so,我们得到结果:

添加时,第一次添加新建一个大小为10的数组,以后不够用时,每次扩大一半,即:10,15,22。。。

每逢这些大小,就重建数组然后把源数据拷过来,这是非常不符合缓冲区的要求的。

而带index的添加:

public void add(int index, E element) {
    rangeCheckForAdd(index);

    ensureCapacityInternal(size + 1);  // Increments modCount!!
    System.arraycopy(elementData, index, elementData, index + 1,
                     size - index);
    elementData[index] = element;
    size++;
}

可见不够时做一次扩张操作,然后再拷贝一次,

拷贝操作最少一次,最多两次。。。喵喵喵???

删除就不讲了,和带index的插入差不多

所以当我们把需要一个缓冲区时得用什么呢?

java.util.concurrent下有许多集合类,你们可以自己看看,我找到的比较好用的:

ArrayList:数组实现,随机查找,随机读操作非常快,写。。。

LinkedList:链表实现,可以快速增删,顺序读取,缺点时随机读取慢,得遍历一遍,单线程时做缓冲区最适合不过

HashSet:没有重复的无序集合(插入重复元素不会理你)

TreeSet:有序集合(红黑树实现)

HashMap:最常用的map类: transient Node<K,V>[] table;  也是数组实现

linkedHashMap:顾名思义,HashMap的链表实现

weakHashMap:不会增加对象的引用计数,所以gc回收时不会考虑它里面的引用

多线程:java.util.concurrent包下有许多多线程支持的工具,不需要自己加锁来控制哦,自带锁机制:

ConcurrentLinkedQueue:多线程安全的 无边界 非阻塞 队列,链表实现

ConcurrentSkipListMap:多线程安全的散列表


版权声明:本文为博主原创文章,转载请注明来源:http://blog.youkuaiyun.com/qq_33213364

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值