JAVA集合(LIST)

不知道怎么开始,先看下JAVA的API(1.8)中LIST集合体系的组成:

LIST实现了两个接口,一个是集合类的父接口,一个是迭代器接口;而子类就比较多,有ArrayList、LinkedList、CopyOnWriteList、Vector等,然后就一个个的来深入了解吧!

ArrayList 源码深入解析

先来看构造方法:

第一个构造方法,需要参数 - 初始化容量,那这个容量用来干嘛呢,可以看到源码中是创建了一个大小为参数数值的对象数组;

第二个构造方法,无需参数,直接等于默认容量的空数组,注意上面的注释"构造一个空集合初始化容量是10";

第三个构造方法是传入一个集合,把集合中的元素方法底层数组中;

通过构造方法可以得知,ArrayList 底层应该是用一个对象数组来存储集合中的元素;

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

private static final Object[] EMPTY_ELEMENTDATA = {};

private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

/** 底层存储集合元素的对象数组 */
/**
 * 这里有一点补充的是,为什么底层数组要用transient修饰符修饰呢?
 * transient修饰符的意思是:该修饰符修饰的变量可以不被序列化,为什么这么做?
 * 因为底层数组的大小(length)和集合的元素个数(size)在很多情况下是不相等的,在这种情况下,序列化 
 * 集合时,如果序列化整个底层数组,就难免会把空的部分也进行序列化,一定程度上浪费空间;而用
 * transient修饰符修饰的变量,可以自定义序列化方式,就不会造成不必要的浪费;
 * 在ArrayList集合中有自己的序列化方法(writeObject,readObject)以及通过transient修饰符来保证序 
 * 列化的合理高效
transient Object[] elementData;

挑几个重点先说,之后慢慢填补!!!

  • ArrayList 的扩容机制

在分析扩容机制之前,应该考虑在什么情况下会需要扩容呢?    
添加元素:add()

public boolean add(E e) {
    /** 扩容流程 */
    ensureCapacityInternal(size + 1);
    elementData[size++] = e;
    return true;
}
/**
 * 所需的最小容量,size+1
 */
private void ensureCapacityInternal(int minCapacity) {
    ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
/** 计算容量 */
private static int calculateCapacity(Object[] elementData, int minCapacity) {
    /** 
     * 计算容量,先判断当前集合底层数组是否为空,如果为空(说明集合第一次添加元素),
     * 则选择默认初识化容量10和扩容容量之间的较大值 
     */
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        return Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    /** 如果不为空,则直接返回扩容容量 */
    return minCapacity;
}
/** 判断是否需要扩容 */
private void ensureExplicitCapacity(int minCapacity) {
    /** 
     * 不管需不需要扩容,都要给计数器自增 - 该变量在后面的fail-fast机制中很重要 
     */
    /** 
     * 这里还有一点需要说明的是:如果批量添加元素时,modCount只会加1,所有该值不能说和size大小一样 
     */
    modCount++;

    /** 如果扩容容量比当前集合底层数组的长度还大,则说明需要扩容,否则不需要 */
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}
/** 扩容关键方法 */
private void grow(int minCapacity) {
    // 当前容量
    int oldCapacity = elementData.length;
    // 扩容1.5倍后的容量大小
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    /** 
     * 比较扩容1.5倍后的容量和所必需的最小容量大小,如果所需的最小容量大于1.5倍容量,
     * 则扩容为最小容量,否则扩容为1.5倍 
     */
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    /** 
     * 再比较经过上述判断的扩容容量和最大数组大小的关系,如果大于最大数组大小(Integer最大值减8),
     * 则扩容容量变为Integer的最大值 
     */
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    /** 通过数组拷贝的方法进行扩容 */
    elementData = Arrays.copyOf(elementData, newCapacity);
}
private static int hugeCapacity(int minCapacity) {
    if (minCapacity < 0) // overflow
        throw new OutOfMemoryError();
    return (minCapacity > MAX_ARRAY_SIZE) ?
        Integer.MAX_VALUE :
        MAX_ARRAY_SIZE;
}
  • ArrayList 的 fail-fast机制(快速失败机制)

arrayList的快速失败机制(fail-fast机制) - java集合框架中的一种错误检测机制
概念:arrayList的fail-fast机制即在对集合进行遍历或者迭代的时候,不能使用集合方法对集合进行结构上的修改,否则会报conCurrentException异常;
再通俗一点就是,遍历集合时不要增加或者删除集合元素;
为什么会有这样一个机制?
原理分析:
在遍历集合的时候,不管我们使用的普通for-each循环还是使用集合特有的迭代器遍历,其实底层都是使用的迭代器
arrayList实现了自己的迭代器:

private class Itr implements Iterator<E> {
    // 遍历元素的游标
    int cursor;       // index of next element to return
    int lastRet = -1; // index of last element returned; -1 if no such
    /**
     * 注意这里会把modCount赋值给expectedModCount,而expectedModCount不会再发生改变
     */
    int expectedModCount = modCount;
    
    public boolean hasNext() {
        return cursor != size;
    }
    
    @SuppressWarnings("unchecked")
    public E next() {
        /** 在遍历获取集合元素时,会进行校验 */
        checkForComodification();
        int i = cursor;
        if (i >= size)
            throw new NoSuchElementException();
        Object[] elementData = ArrayList.this.elementData;
        if (i >= elementData.length)
            throw new ConcurrentModificationException();
        cursor = i + 1;
        return (E) elementData[lastRet = i];
    }
    
    // 迭代器内部提供了删除元素的方法,使用该方法不会出现快速失败
    public void remove() {
        if (lastRet < 0)
            throw new IllegalStateException();
        /** 在遍历获取集合元素时,会进行校验 */
        checkForComodification();
    
        try {
            ArrayList.this.remove(lastRet);
            cursor = lastRet;
            lastRet = -1;
            // 删除元素之后,会重新把modCount的值赋给expectedModCount
            expectedModCount = modCount;
        } catch (IndexOutOfBoundsException ex) {
            throw new ConcurrentModificationException();
        }
    }

    /**
     * 每次遍历元素时都会校验modCount和expectedModCount的值是否一致,如果不相等,则抛出异常
     * 因为expectedModCount的值是在迭代器内部把modCount的值赋予给的,不会改变,所以两个值不相等的情况肯定是modCount发生了改变
     * 而结合前面arrayList的扩容部分的内容,可以知道在集合添加元素或者删除元素的时候modCount的值会变
     */
    final void checkForComodification() {
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
    }
}

还在想,还有啥可以补充的!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值