18.2 ArrayList(底层源码解析)

本文详细讲解了ArrayList和Vector在Java集合体系中的底层实现、动态数组概念、构造方法中的空数组区别,以及元素添加与容量管理的机制。重点讨论了为何构造方法中有不同空数组处理策略,以及数组扩容的原理和过程。
摘要由CSDN通过智能技术生成

一、集合体系

1.1 Collection体系
1.1.1 ArrayList & Vector
1)底层实现和特点

底层实现:动态数组实现

特点:元素有序且可重复。Vector是线程安全,ArrayList是线程不安全的。

关系:Vector是JDK1.0就有个类,但是现在所学习的集合体系是JDK1.2才产生的。因此在JDK1.2之后,sun公司强行的让Vector实现了List接口。所以Vector内部出现了很多工程重复的方法。

2)什么是动态数组?

注意:Java中本质没有动态数组的,一旦数组的长度确定就不可改变(原因是因为数组是一段连续的内存地址),那为什么不设置一个逻辑连续,物理不连续?这样的数组是没有办法通过数组下标定位的。下标1万和找下标为0的速度是一样快的。因为他在内存中是连续的地址,只要在最开始的元素位置只要加上1万的4个字节就是4万个字节就你够立马找到。

动态数组的实现:一旦原来的数组空间不够时,创建一个长度更长的新数组,然后将旧的数组元素移动到新数组中,以此来实现数组的"动态扩容",旧的数组会因为没有引用,而被垃圾回收器回收掉

3)ArrayList源码解析

基本属性介绍

/**
 * 默认的初始化容量
 */
private static final int DEFAULT_CAPACITY = 10;

/**
 * 
 */
private static final Object[] EMPTY_ELEMENTDATA = {};

/**
 * 默认的空节点数组 
 */
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

/**
 * 底层的核心动态数组的引用
 */
transient Object[] elementData; // non-private to simplify nested class access

/**
 * 数组的元素个数(不是数组的长度)
 */
private int size;

构造方法

/**
有参构造方法。
参数为初始化数组的长度
*/
public ArrayList(int initialCapacity) {
    if (initialCapacity > 0) {
        //直接初始化指定长度的数组,赋值给elementData
        this.elementData = new Object[initialCapacity];
    } else if (initialCapacity == 0) {
        this.elementData = EMPTY_ELEMENTDATA;
    } else {
        throw new IllegalArgumentException("Illegal Capacity: "+
                                           initialCapacity);
    }
}

/**
无参构造方法
*/
public ArrayList() {
    //直接将默认的空数组赋值给elementData变量
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

疑问:为什么在两个构造方法给了不同的空数组?


因为从道理将给同一个是没有问题的,但是从设计角度来讲,这两个数组他会有一定的区别,一个是当数组参数零也就是指定了容量为0进行赋值空节点数组给elementData
,一个是默认参数为0赋值空节点数组给elementData,这个版本来看作用是同一个,但是当以后版本出现了变化是当数组参数零给的是进行列外一个操作这样DEFAULTCAPACITY_EMPTY_ELEMENTDATA和EMPTY_ELEMENTDATA就互不影响了。就是为了以后的扩展

元素添加(add)

/**
添加元素
*/
public boolean add(E e) {
    //判断是否容量足够,如果不够就需要扩容
    ensureCapacityInternal(size + 1); 
    //将元素e放入底层数组,下标为size的位置,然后size自增
    elementData[size++] = e;
    return true;
}

/**
容量判断
参数:本次至少需要的容量大小
*/
private void ensureCapacityInternal(int minCapacity) {
     ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}

/**
重新计算数组需要的长度,如果是第一次添加元素,则返回默认长度10
*/
private static int calculateCapacity(Object[] elementData, int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }

/**
判断是否需要扩容
*/
private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        //如果需要的最小容量超过了底层数组的实际长度,则进行扩容
        if (minCapacity - elementData.length > 0)
            //扩容方法
            grow(minCapacity);
    }

/**
数组扩容的方法
参数:为至少需要的容量大小
*/
private void grow(int minCapacity) {
        //旧的容量
        int oldCapacity = elementData.length;
        //新的容量(按照原容量1.5倍扩容),右移一位相当于除以二,左移一位相当于乘以二
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        //扩容的容量如果没有达到最小容量
        if (newCapacity - minCapacity < 0)
            //直接将最小容量变成新容量
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        //重要:数组扩容的方式
        //copyOf - 将参数1的数组元素,拷贝到一个新数组中,新数组的容量为参数2,并且将新数组返回
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

注意:copyOf底层是有native本地方法实现的,Java所有的native方法都是调用c/c++实现的,为什么要用c来实现的,因为java是没有办法操作堆的都是通过栈操作堆的,用c直接操作物理内存的比用c速度快的多。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值