Java基础你要知道的事情------集合框架之ArrayList

本文源码JDK版本是1.7

1.ArrayList是用来存储数据的数据结构线性的,底层是由一个动态数组来维护的,可以动态扩容,然后他是有序的存储什么顺序读取就是什么顺序,可以存储null,可以存储重复的数据,是线程非安全的。

2.ArrayList的继承实现结构图,继承了AbstractList实现了List接口,还有实现了一些其他的需要用到的功能接口如序列化,快速访问等.**

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
/**RandomAccess,Serializable这2个接口都是空的,其实只是起到一个标志的作用,说明这个类支持快速访问,可以进行序列化跟反序列化。
Cloneable接口,表明这个是可以进行浅拷贝的,是可以调用Object.clone()返回该对象的浅拷贝。
什么是浅拷贝呢?
举个例子:
假设x是一个非空对象,应该有:
(x.clone()!=x )==true,也就是说它们不是同一个对象。
(x.clone().getClass()==x.getClass())==true,也就是它们是同一个类型的class,
(x.equals(x.clone()))==true,也就是,逻辑上和内容上,它们应该是相同的。
实现了Cloneable的类应该要满足上面的几个情况。
**/
}

3.ArrayList最基本几个方法

初始化的几个方法

无参数的

public ArrayList() {
        super();
        this.elementData = EMPTY_ELEMENTDATA;
    }

带初始大小的

public ArrayList(int initialCapacity) {
        super();
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        this.elementData = new Object[initialCapacity];
    }

带集合参数的

public ArrayList(Collection<? extends E> c) {
        elementData = c.toArray();
        size = elementData.length;
        // c.toArray might (incorrectly) not return Object[] (see 6260652)
        if (elementData.getClass() != Object[].class)
            elementData = Arrays.copyOf(elementData, size, Object[].class);
    }

ArrayList的源码分析—-add(),add(index,elementData)添加元素

//下面是需要用到的一些初始化数据其中有个transient 关键字啥意思啊。。就是不允许序列化(不允许序列化我从哪里拿来的元素序列化的啊????还实现了序列化接口搞事情)

private transient Object[] elementData;

//ArrayList中有个方法如下:

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);

        // 疑问你为了节省不序列化为空的位置,这边循环了一次,是不是影响了效率。。还是说读的效率很高这点小小循环的无所谓。。
        for (int i=0; i<size; i++) {
            s.writeObject(elementData[i]);
        }

        if (modCount != expectedModCount) {
            throw new ConcurrentModificationException();
        }
    }

//解释如下:
  //这就跟这个ArrayList的特性有关,我们知道ArrayList的容量,也就是这个数组的容量,一般都是预留一些容量,等到容量不够时再拓展,那么就会出现容量还有冗余的情况,如果这时候进行序列化,整个数组都会被序列化,连后面没有意义空元素的也被序列化。这些是不应该被存储的。所以java的设计者,就为这个类提供了一个writeObject方法,在实现了Serializable接口的类,如果这个类提供了writeObject方法,那么在进行序列化的时候就会通过writeObject方法进行序列化,所以ArrayList的writeObject方法就会显式的为每个实际的数组元素进行序列化,只序列化有用的元素。


private static final Object[] EMPTY_ELEMENTDATA = {};
private static final int DEFAULT_CAPACITY = 10;
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
//添加入口
public boolean add(E e) {
        ensureCapacityInternal(size + 1);  //JDK1.7中默认无参构造器size是0,第一次add的时候形参是0+1=1
        elementData[size++] = e;//添加元素,size自增
        return true;
    }
//扩容
 private void ensureCapacityInternal(int minCapacity) {//minCapacity=1
        if (elementData == EMPTY_ELEMENTDATA) {//第一次调用add方法,我们new的时候list的容量是0是个空数组,在这边给他赋值最小容量是10
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        ensureExplicitCapacity(minCapacity);
    }

private void ensureExplicitCapacity(int minCapacity) {//1,至少需要这么大的容量
        modCount++;//这个的作用查了下是ArrayList也采用了快速失败的机制,通过记录modCount参数来实现。在面对并发的修改时,迭代器很快就会完全失败,而不是冒着在将来某个不确定时间发生任意不确定行为的风险。有时间再看看

        if (minCapacity - elementData.length > 0)//至少需要的容量比数组的长度来的大才需要扩容
            grow(minCapacity);
    }

 private void grow(int minCapacity) {//真正扩容的代码

        int oldCapacity = elementData.length; //老数组容量
        int newCapacity = oldCapacity + (oldCapacity >> 1);//扩容后新数组的容量 jdk1.7用了效率更高的位运算计算新扩容后的数组的长度
        if (newCapacity - minCapacity < 0)   //假设新数组容量比最小需要的长度小,扩容大小就按照最小的实际需要给
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)//超过Math最大值-8的话,看你最小需要的大小是不是比MAX_ARRAY_SIZE 这个大,大的话就给最大值,否则就是MAX_ARRAY_SIZE 
            newCapacity = hugeCapacity(minCapacity);
        elementData = Arrays.copyOf(elementData, newCapacity);//调用Arrays的静态方法,造一个newCapacity大小的数组然后把以前的copy过去
    }

 private static int hugeCapacity(int minCapacity) {//jdk1.7之前没有Integer.MAX_VALUE这个东西
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }

详细解释下这句扩容代码:int newCapacity = oldCapacity + (oldCapacity >> 1);(JDK1.7的版本)
之前的版本有些是这么写的:int newCapacity = (oldCapacity * 3)/2 + 1;

看到扩容的时候把元素组大小先乘以3,再除以2,最后加1。为什么?我们可以想:

1、如果一次性扩容扩得太大,必然造成内存空间的浪费

2、如果一次性扩容扩得不够,那么下一次扩容的操作必然比较快地会到来,这会降低程序运行效率,要知道扩容还是比价耗费性能的一个操作

还没写完。。。。。。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值