ArrayList集合源码分析

ArrayListList集合分析


加粗样式


前言

集合是Java的基础知识,在面试过程中经常被问到集合相关的知识,在工作中我们经常用到List和Map,List又分为ArrayList、LinkedList和Vector,本文简单的对ArrayList集合进行了讲解。

一、核心属性

首先我们看一下ArrayList有哪些属性:

  • DEFAULT_CAPACITY: 默认初始容量的大小,值为10。
  • EMPTY_ELEMENTDATA: 这是一个空数组,用来初始化集合内部的elementData。
  • DEFAULTCAPACITY_EMPTY_ELEMENTDATA: 同样是一个空数组,用来初始化集合内部的elementData,咱也不知道为啥搞俩。
  • elementData: 用来存放具体内容数据的。
  • size: 记录着ArrayList当前存放数据长度。
  • MAX_ARRAY_SIZE: 最大容量大小,值为int的最大值-8。

二、核心方法

下面看一下ArrayList中我们常用的的方法是怎么实现的。

构造方法:

如果是无参构造方法的话只是对elementData做了个初始化赋值。

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

传递int的有参构造方法,如果大于0的话,初始化为传递参数大小的数组;如果等于0,初始化为空数组;如果小于0报异常。

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

传递集合的构造方法,对其内部的数组进行拷贝赋值给elementData;如果传递的集合大小为0,则初始化为空数组。

    public ArrayList(Collection<? extends E> c) {
        Object[] a = c.toArray();
        if ((size = a.length) != 0) {
            if (c.getClass() == ArrayList.class) {
                elementData = a;
            } else {
                elementData = Arrays.copyOf(a, size, Object[].class);
            }
        } else {
            elementData = EMPTY_ELEMENTDATA;
        }
    }

add()方法

add()方法是我们本次分析的重点,先看一下源码:

public boolean add(E e) {
	//对数组进行扩容
    ensureCapacityInternal(size + 1);  
    //赋值
    elementData[size++] = e;
    return true;
 }

重点看一下扩容机制:

 private void ensureCapacityInternal(int minCapacity) {
      ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
  }
 //如果数组大小为空的话,在默认容量和1之间去一个最大值,最终设置的默认大小为10;如果不为空的话返回的为size+1。
 private static int calculateCapacity(Object[] elementData, int minCapacity) {
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
         return Math.max(DEFAULT_CAPACITY, minCapacity);
     }
     return minCapacity;
 }
 //用size+1与上次设置的数组的长度做判断是否需要扩容
 private void ensureExplicitCapacity(int minCapacity) {
     modCount++;
     if (minCapacity - elementData.length > 0)
         grow(minCapacity);
  }
  //扩容机制
 private void grow(int minCapacity) {
	  //记录下原来数组的大小
      int oldCapacity = elementData.length;
      //设置新的数组大小为原来数组大小的1.5倍,右移一位代表着除以2
      int newCapacity = oldCapacity + (oldCapacity >> 1);
      //新的数组大小和size+1之间取最大值,size为0和1的时候会遇到
      if (newCapacity - minCapacity < 0)
          newCapacity = minCapacity;
      //如果数组容量比int最大值-8大,则将数组容量扩充为int的最大值
      if (newCapacity - MAX_ARRAY_SIZE > 0)
          newCapacity = hugeCapacity(minCapacity);
      //进行数组拷贝,扩大数组
      elementData = Arrays.copyOf(elementData, newCapacity);
  }

get()方法

get()方法是取集合中的内容使用的,这个比较简单

	//首先判断是否数组越界
   public E get(int index) {
        rangeCheck(index);
        return elementData(index);
    }

remove()方法

remove()方法是集合用来溢出元素的,有传递下表和对象两种。

  public E remove(int index) {
  		//判断传参是否数组越界
        rangeCheck(index);
        modCount++;
        //取出要移除的元素
        E oldValue = elementData(index);
        //下面部分则是数组的拷贝,将下标位置后面的数据往前移动一位
        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        //数组完成拷贝后将最后一位置为空
        elementData[--size] = null; 
        return oldValue;
    }

传递对象的移除

    public boolean remove(Object o) {
        if (o == null) { //传递为空不说了
            for (int index = 0; index < size; index++)
                if (elementData[index] == null) {
                    fastRemove(index);
                    return true;
                }
        } else {
            for (int index = 0; index < size; index++)
            	//不为空遍历找到该对象的下标位置
                if (o.equals(elementData[index])) {
                	//这个方法又是个数组拷贝,同上面后半截一样
                    fastRemove(index);
                    return true;
                }
        }
        return false;
    }

还有size()方法,就是返回size属性这就不用说了。

三、ArrayList和Vector的区别

Vector的源码和ArrayList的没多少区别,感兴趣的自己可以自己看一下。通过看源码发现ArrayList与Vector内部都是基于数组实现的,但是两个之间有一些区别的地方是面试常问的。

  • 看构造方法,ArrayList创建的是空数组,直到add的时候才初始化容量为10;而Vector在创建的时候就直接初始化数组容量为10了。
  • 看扩容机制,ArrayList的扩容机制是1.5倍扩容;而Vector的扩容机制是默认二倍扩容,构造方法还可以传参指定扩容倍数。
  • 在Vector中使用synchronized修饰符,其实线程安全的,当然相对的效率就低;ArrayList是线程不安全的,但是他的效率相对就高。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值