ArrayList详解

Arrary的原理:ArrayList是由数组实现的,可以动态进行扩容。

用无参的构造函数创建list时,list的长度是0,也就是个空的数组,

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

但是当添加元素之后,也就是使用add()方法,由于现在数组长度为0,会触发扩容机制,这时候会扩容为10(这是最小的数组长度)

public boolean add(E e) {
        ++this.modCount;//记录这个list的元素修改次数
        this.add(e, this.elementData, this.size);
        return true;
    }

ArrayList的扩容底层原理:
使用Array.copyOf时,在内部又创建了一个长度为newlength的数组,调用System.arraycopy()方法,将原来数组中的元素复制到了新的数组中。

public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
        T[] copy = ((Object)newType == (Object)Object[].class)
            ? (T[]) new Object[newLength]
            : (T[]) Array.newInstance(newType.getComponentType(), newLength);
        System.arraycopy(original, 0, copy, 0,
                         Math.min(original.length, newLength));
        return copy;
    }

System.arraycopy()方法。该方法被标记了native,调用了系统的C/C++代码,在JDK中是看不到的,但在openJDK中可以看到其源码。该函数实际上最终调用了C语言的memmove()函数,因此它可以保证同一个数组内元素的正确复制和移动,比一般的复制方法的实现效率要高很多,很适合用来批量处理数组。Java强烈推荐在复制大量数组元素时用该方法,以取得更高的效率。

看博客看到一段关于size和modeCount的解释:

size和modCount的区别
可能看了源码有时候还分不清size和modCount的区别,那么这里就用例子来说明。
size是ArrayList的变量。modCount是ArrayList的父类AbstractList中的变量,默认值为0。
size记录了ArrayList中元素的数量,modCount记录的是关于元素的数目被修改的次数。modCount在ArrayList的普通操作里可能并没有看出多大用处,但是在涉及到fail-fast就主要是依靠它了相关解释

ArrayList的扩容机制:

扩容时,每次都是原来长度的1.5倍(最小的数组长度是10),如果在扩容后的数组长度还是不够,会直接用需要扩容到的长度来用
(例如,原来长度是10,需要增加10个元素。
源码里是这样处理的,newCapacity =10+10>>1 =》newCapacity = 10+10/2也就是10+5=15),但是如果长度还是不够的话,也就是newCapacity <minCapacity,会直接用需要的最小扩容长度(minCapacity),也就是20.

 private int newCapacity(int minCapacity) {
        int oldCapacity = this.elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1);//这里就是扩容长度,将原来的长度向右移位,也就是除以2。
        if (newCapacity - minCapacity <= 0) {
            if (this.elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
                return Math.max(10, minCapacity);//当是个空数组时,会给最小的10位为数组长度。
            } else if (minCapacity < 0) {
                throw new OutOfMemoryError();
            } else {
                return minCapacity;
            }
        } else {//这里-2147483639 是怕newCapacity超过int的最大值,如果没有超过的话,就直接用了newCapacity这个值来做数组长度(20),否则使用最大的int的值来做数组长度
            return newCapacity - 2147483639 <= 0 ? newCapacity : hugeCapacity(minCapacity);
        }
    }

关于(oldCapacity >> 1)的解释

int len = 10;
int newLen = len >> 1;
System.out.println(newLen);
把10 转换成二进制为1010 向右移动一位 为 101 转为10进制等于5 加上原来的长度就扩容1.5倍

对于线程不安全的解释:

线程不安全的解释

解决线程问题:
两种方法


//Synchronized对代码进行加锁,力度大,所以代码执行效率低下
List<String> list = Collections.synchronizedList(new ArrayList<String>());
//写时复制通过lock机制进行枷锁
/*CopyOnWrite容器即写时复制的容器。往一个容器添加元索的时候,不直接往当前容器Object[]添加,
而是先将当前容器Object[]进行Copy,复制出一个新的容器object[] newElements,
然后往新的容器object[] newElements 里添加元素,
添加完元素之后,再将原容器的引用指向新的容器setArray(newElements);。 
这样做的好处是可以CopyOnWrite容器进行并发的读,而不需要加锁,因为当前容器不会添加任何元素。
所以CopyOnWrite 容器也是一种读写分离的思想,读和写不同的容器*/
List<String> list = new CopyOnWriteArrayList();//这个很损耗性能,因为要开辟多一个内存空间

实现了Serializable接口,因此它支持序列化,能够通过序列化传输
RandomAccess接口,支持快速随机访问

https://blog.csdn.net/zhangxin961304090/article/details/46801225

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值