前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >java之ArrayList源码分析

java之ArrayList源码分析

作者头像
码农王同学
发布2019-10-23 16:40:55
3050
发布2019-10-23 16:40:55
举报
文章被收录于专栏:后端Coder后端Coder
从事javaWeb后端开发,集合的用处可以说是到处可见,数组由于大小是固定的,相对于动态扩容的集合来说,就用的不是那么多了。

今天我们就来分析下ArrayList集合类的源码了,希望可以帮助到你,首先我们还是从集合的入口慢慢深入分析吧。还是一贯风格,看代码咯。

代码语言:javascript
复制
 List<String> stringList=new ArrayList<>();
 我们先直接new一个集合对象,接下来我们通过IDE工具直接进去代码

通过new ArrayList(),我们跟进了这样的代码。

Tips:左右滑动可以看完整代码信息。

代码语言:javascript
复制
 /**
     * Constructs an empty list with an initial capacity of ten.
     */
  public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
  }
  private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

上面的方法就是创建一个空集合,所以在new ArrayList()的时候就帮我们创建了一个空数组,我们还是看下elementData是如何定义的。

代码语言:javascript
复制
transient Object[] elementData; //这是一个Object[]数组用于存储任何类型的数据

好了,下面我们继续看下,我们在使用集合的add()方法时是个怎么样的过程吧,还是直接看程序代码。

代码语言:javascript
复制
 
 public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
  }

上面的add()方法,就是将需要添加的元素e,添加到集合的末尾,也就是数组的末尾了,不过在看下面的流程之前,我们还是先分析一下ensureCapacityInternal()这个方法。

代码语言:javascript
复制
private void ensureCapacityInternal(int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }

        ensureExplicitCapacity(minCapacity);
    }
   private static final int DEFAULT_CAPACITY = 10;//默认的数组容量

这两个方法主要是判断传入的size大小与默认值10进行比较,获取两者之中的最大值,然后调用ensureExplicaitCapacity()方法。

接下来我们可以ensureExplicaitCapacity()方法的代码主要做了什么。代码继续。

代码语言:javascript
复制
      private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

上面的方法实现的是对传入的值和数组的length进行比较,满足条件直接进入grow()这个方法了,接下来我们继续看下grow()这个方法吧,看这样的代码需要一些耐心。

代码语言:javascript
复制
     private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

这个方法的功能就是,增加容量,以确保它至少可以容纳最小容量参数指定的元素,然后调用Arrays.copyOf()进行拷贝。其实我们再往下面进行代码的分析,就是下面这个方法的调用的。

Tips:左右滑动可以看完整代码信息。

代码语言:javascript
复制
System.arraycopy(original, 0, copy, 0,Math.min(original.length, newLength));
代码语言:javascript
复制
public static native void arraycopy(Object src,  int  srcPos,Object dest, int destPos,int length);

由于arraycopy是由native关键字修饰的,它是一个本地方法,而不是一个java方法,所以它是运行在本地方法栈的,而不是java栈,建议先进行了解下面的文章java内存区域划分详解好区分本地方法栈和java栈的区别。

上面的就是调用ArrayList类的add()方法的整个流程了,下面我们再看看其他方法吧。

先看下isEmpty()方法的代码。这个比较简单理解,代码看上去,一目了然。

代码语言:javascript
复制
public boolean isEmpty() {
        return size == 0; //判断size大小是否为0,是不是和你写的代码一样,坏笑
 }

下面继续分析其他方法吧,既然说到今天分享的是ArrayList类的代码分析,不能草草了事对不,也是自己公众号的初衷。

我们调用集合的size()方法就对应着下面的方法,这个size就是数组里面的元素个数大小。

代码语言:javascript
复制
 public int size() {
        return size;
  }

看下集合是否包含某个元素的contains()方法咯。

代码语言:javascript
复制
public boolean contains(Object o) {
        return indexOf(o) >= 0;
 }
 在这里我们看下indexOf()方法做了什么事情。
代码语言:javascript
复制
   public int indexOf(Object o) {
        if (o == null) {
            for (int i = 0; i < size; i++)
                if (elementData[i]==null)
                    return i;
        } else {
            for (int i = 0; i < size; i++)
                if (o.equals(elementData[i]))
                    return i;
        }
        return -1;
    }

上面的indexOf()方法,主要是对传入的对象obj分两种情况进行循环遍历判断,因为add()方法可以添加null值进去的,所以数组里面的元素可能就包含null值。

这时就需要我们按照两种方式,一个是对null值判断,另外一个是正常的元素值,循环遍历整个数组进行一一比较,返回对应的数组的下标值,如果两种遍历都没有找到就返回-1,所以contains()方法调用indexOf()方法就是对返回的数组下标进行了判断。

接下来我们还是慢慢分析吧,由于集合还是比较重要的,需要一点耐心。

我们看下集合的clear()方法的代码。

代码语言:javascript
复制
public void clear() {
        modCount++;

        // clear to let GC do its work
        for (int i = 0; i < size; i++)
            elementData[i] = null;

        size = 0;
    }

clear()方法的功能是循环整个集合(数组)的大小,将每个集合元素都赋值为null,size赋值为0,这样就让JVM的GC机制来帮我们进行无用对象实例进行清除了,建议先移步到这java虚拟机,应该了解一点点先了解一下什么是JVM,本来不打算写下GC了,看来还是有必要的,后面构思一下,再写咯。

喜欢文章的可以关注,转发和分享一下文章,接下来我们看下集合的addAll()方法吧。

Tips:左右滑动可以看完整代码信息。

代码语言:javascript
复制
public boolean addAll(Collection<? extends E> c) {
        Object[] a = c.toArray();//先将集合c转换为数据
        int numNew = a.length;
        //和上面的代码分析一样
        ensureCapacityInternal(size + numNew);  // Increments modCount
        System.arraycopy(a, 0, elementData, size, numNew);//进行数组元素的拷贝
        size += numNew;
        return numNew != 0;
    }

ok,addAll()方法的功能解释已在代码对应位置说明了。

继续看下根据索引值获取对应元素的get()方法的代码吧,方法的功能和实现在代码中已说明,看注释信息就行了。

代码语言:javascript
复制
public E get(int index) {
        rangeCheck(index);//这个就是我们常用的入参校验,我们看下它做了什么

        return elementData(index);//这个主要是返回数组对应下标的元素值
  }
  
   private void rangeCheck(int index) {
   //判断索引下标是否大于size值,若大于直接抛出索引越界异常信息,或许你遇到过这样的异常信息
        if (index >= size)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }

既然数组可以转为集合,那么集合也可以转为数组,我们继续看下集合是如何转换为数组的方法toArray()方法。

代码语言:javascript
复制
public Object[] toArray() {
        return Arrays.copyOf(elementData, size);
 }
代码语言:javascript
复制
public static <T> T[] copyOf(T[] original, int newLength) {
        return (T[]) copyOf(original, newLength, original.getClass());
}

我们继续看下Arrays.copyOf()方法,这里面涉及到静态工厂方法的内容,建立先看这部分内容java的静态工厂方法,先自己了解下。

其实下面调用的是个静态方法,进行数组之间的拷贝,返回数组。

代码语言:javascript
复制
public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
        @SuppressWarnings("unchecked")
        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;
    }

集合的返回集合元素下标的indexOf()方法在上面的分析过程中已讲解。

上面我们说了indexOf()方法,接下来我们看下集合的lastIndexOf()方法。

代码语言:javascript
复制
public int lastIndexOf(Object o) {
        if (o == null) {
            for (int i = size-1; i >= 0; i--)
                if (elementData[i]==null)
                    return i;
        } else {
            for (int i = size-1; i >= 0; i--)
                if (o.equals(elementData[i]))
                    return i;
        }
        return -1;
    }

是不是和indexOf()方法的写法很像,一个是从前往后遍历,一个是从后往前遍历进行判断,找不到就返回-1,同样的套路操作,希望可以帮助到你。

下面我们再看最后一个集合的remove()方法吧。

代码语言:javascript
复制
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;
    }

集合的删除方法和indexOf()方法等都一样,都需要区分null值和非null值的,不过这里面调用了共同的fastRemove()方法,我们继续看下fastRemove()方法。

代码语言:javascript
复制
private void fastRemove(int index) {
        modCount++;
        int numMoved = size - index - 1;//这里的计算是为下面数组元素的拷贝做准备的
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; //这里将对应的数组元素设置为null,让JVM的GC去清除无用的数组元素,进行内存空间的回收
    }

好了,关于ArrayList集合类的源码分析到这里就结束了,希望这篇文章可以帮助到你

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-10-21,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 码农王同学 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
腾讯云代码分析
腾讯云代码分析(内部代号CodeDog)是集众多代码分析工具的云原生、分布式、高性能的代码综合分析跟踪管理平台,其主要功能是持续跟踪分析代码,观测项目代码质量,支撑团队传承代码文化。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档