Java源码初学_ArrayList

一.ArrayList的构造器和构造方法

  在ArrayList中定义了两个空数组,分别对应当指定默认构造方法时候,指向的数组已经给定容量,但是容量等于0的时候,指向的数组.此外在构造函数中传入Collection对象的时候,直接调用对象的toArray方法,并且将容器内部的引用指向得到的数组.源代码如下:

private static final long serialVersionUID = 8683452581122892189L;

    /**
     * 默认的数组的容量
     */
    private static final int DEFAULT_CAPACITY = 10;

    /**
     * 在默认构造函数中给定的空对象数组
     */
    private static final Object[] EMPTY_ELEMENTDATA = {};

    /**
      在有参数的构造函数中,如果参数为0,使用这个空对象数组
     */
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    /**
     装元素的数组
     */
    transient Object[] elementData; // non-private to simplify nested class access

    /**
     * 容器大小
     *
     * @serial
     */
    private int size;

    /**
     *如果指定容量>0.new一个object数组
      如果==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);
        }
    }

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

       public ArrayList(Collection<? extends E> c) {
        elementData = c.toArray();
        if ((size = elementData.length) != 0) {
            //toArray方法可能不会返回Object数组
            if (elementData.getClass() != Object[].class)
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
            // 将引用指向空数组.
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }

二.动态调整大小(trimToSize方法)

  该方法使得容器内部的数组的长度和其储存元素的数目相同,通过调用Arrays.copyOf方法(在ArrayList的实现中大量用到了这个方法),完成了将容器内部的引用变量指向一个新的长度为容器中元素的个数的数组.

public void trimToSize() {
        modCount++;//属于对容器的容量进行修改的操作,modCount+1
        if (size < elementData.length) {
            elementData = (size == 0)
              ? EMPTY_ELEMENTDATA
              : Arrays.copyOf(elementData, size);
        }
    }

三.ensureCapacity方法

  这个方法增加 ArrayList 实例的容量,以确保它至少能够容纳最小容量参数所指定的元素数。需要注意的是,当数组引用指向默认构造函数的空数组的时候,将会判断是否需要增加的容量<10,如果小于10,则直接增加数组的长度至10.当数组的长度不足以容纳元素的时候,会直接将数组的长度变为原来的1.5倍.代码如下:

public void ensureCapacity(int minCapacity) {
        int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
            //如果不是默认的构造函数,下限为0
            ? 0
            // 如果是默认的构造函数.那么下限为10
            // 在给定的容量小于10的情况下,不需要扩容
            : DEFAULT_CAPACITY;

        if (minCapacity > minExpand) {
            ensureExplicitCapacity(minCapacity);
        }
    }

    private void ensureCapacityInternal(int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }

        ensureExplicitCapacity(minCapacity);
    }

    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

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

    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

    private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)//旧容量的1.5倍<mincapacity的时候,才使用mincapacity
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        //elementData指向一个copy好的数组
        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;
    }

四.indexOf方法

  由于ArrayList方法底层是数组的实现,因此对于AbstractList的indexOf方法提供了复写,底层采用了数组来实现该方法.

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

五.add方法

  ensureCapacity保证了如果是默认构造函数,那么建立10个容量的数组,不是的话,则数组即将满的时候,扩容数组.(新数组的容量一般情况下都是老数组容量的1.5倍).

public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // 增加modCount以及容量~
        elementData[size++] = e;
        return true;
    }

六.add(int index,E element)方法

  该方法采用System.arrayCopy方法将index位置及以后的元素全部拷贝到index+1的位置处,并且将数组的长度加1.随后在index索引处赋值.

public void add(int index, E element) {
        rangeCheckForAdd(index);

        ensureCapacityInternal(size + 1);
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
        elementData[index] = element;
        size++;
    }

七.addAll方法.

  采用将Collection集合转化为数组的方式,在调用System.ArrayCopy方法来完成对于数组的增加.

public boolean addAll(Collection<? extends E> c) {
        Object[] a = c.toArray();
        int numNew = a.length;
        ensureCapacityInternal(size + numNew);  // 增加modCount的值
        System.arraycopy(a, 0, elementData, size, numNew);
        size += numNew;
        return numNew != 0;
    }

八.removeAll和retainAll方法

  removeAll和retainAll方法通过向同一个方法传入不同的参数,实现移除元素,方法直接在数组对象中记录与给定Collection对象相同的或者不同的元素.

public boolean removeAll(Collection<?> c) {
        Objects.requireNonNull(c);
        return batchRemove(c, false);
    }
    public boolean retainAll(Collection<?> c) {
        Objects.requireNonNull(c);
        return batchRemove(c, true);
    }
//在此方法中进行removeAll和retainAll方法的实现:
    private boolean batchRemove(Collection<?> c, boolean complement) {
        final Object[] elementData = this.elementData;//建立一个数组类型的引用指向内存中的数组对象
        int r = 0, w = 0;//r遍历数组的索引.w记录相同的元素或者不同的元素
        boolean modified = false;
        try {
            for (; r < size; r++)
                if (c.contains(elementData[r]) == complement)
                    elementData[w++] = elementData[r];
        } finally {
            if (r != size) {
                System.arraycopy(elementData, r,
                                 elementData, w,
                                 size - r);
                w += size - r;
            }
            if (w != size) {
                // clear to let GC do its work
                for (int i = w; i < size; i++)
                    elementData[i] = null;        //w索引之前记录了相同的或者不同的元素
                modCount += size - w;
                size = w;
                modified = true;
            }
        }
        return modified;
时间: 2024-10-12 02:10:24

Java源码初学_ArrayList的相关文章

Java源码初学_AbstractList&amp;AbstractCollection

一.AbstractCollection抽象类:(提供了Collection接口的骨干实现,以减少实现接口所需要的工作) 1.contains方法 contains方法,通过迭代器对于列表的每一个元素进行遍历,并且判断是否与给定的元素相等.另外由于传入的元素可能为null,因此在执行传入的元素的equals方法的时候,需要先判断是否为null.源代码如下: public boolean contains(Object o) { Iterator<E> it = iterator(); if (

Java源码初学_HashSet&amp;LinkedHashSet

一.概述 HashSet是建立在HashMap的基础上的,其内部存在指向一个HashMap对象的引用,操作HashSet实际上就是操作HashMap,而HashMap中所有键的值都指向一个叫做Dummy value的值.这是一个Object对象.是在HashSet进行显示初始化的时候初始化出来的,而HashMap在构造函数中被创建出来.二.HashMap在HashSet方法中的使用及LinkedHashMap HashSet是建立在HashMap的基础上的,它的几乎所有的操作都是通过HashMa

Java源码初学_LinkedList

一.LinkedList的内部数据结构 LinkedList底层是一个链表的数据结构,采用的是双向链表,基本的Node数据结构代码如下: private static class Node<E> { E item; //节点放置的元素 Node<E> next; //下一节点 Node<E> prev; //上一结点 Node(Node<E> prev, E element, Node<E> next) { this.item = element

Java源码初学_LinkedHashMap

一.概述: LinkedHashMap是HashMap的子类,它的基本操作与HashMap相同,与之不同的是,它可以实现按照插入顺序进行排序.也可以通过在构造函数中指定参数,按照访问顺序排序(LinkedHashSet无法按照访问顺序进行排序). 二.LinkedHashMap是如何实现按照插入顺序进行排序的? LinkedHashMap在Entry内部类中动了一点小手脚…实际上,LinkedHashMap所有的元素可以构成一个以header(Entry对象)为头的双向链表.在HashMap中有

如何阅读Java源码 阅读java的真实体会

刚才在论坛不经意间,看到有关源码阅读的帖子.回想自己前几年,阅读源码那种兴奋和成就感(1),不禁又有一种激动. 源码阅读,我觉得最核心有三点:技术基础+强烈的求知欲+耐心. 说到技术基础,我打个比方吧,如果你从来没有学过Java,或是任何一门编程语言如C++,一开始去啃<Core Java>,你是很难从中吸收到营养的,特别是<深入Java虚拟机>这类书,别人觉得好,未必适合现在的你. 虽然Tomcat的源码很漂亮,但我绝不建议你一开始就读它.我文中会专门谈到这个,暂时不展开. 强烈

[笔记&amp;轮子]java源码 生成本地javadoc api文档

在用Eclipse写java代码时候,有时候因为不知道一个java函数的作用,会通过把鼠移动到java函数上,如果它有javadoc的相关内容就会显示出来.但是并非所有java代码都有javadoc:即使安装了javadoc,在eclipse中如果不进行设定,也可能无法使用. 我在win7下安装的是javase的jdk,发现eclipse中默认的javadoc路径是http://download.oracle.com/javase/7/docs/api/,显然这是一个在线资源,问题是网络总是不稳

一个android dex 转java源码工具

和dex2jar,smali2java比起来,这个工具至少结果是正确的,前两者对于循环,异常等的处理明显逻辑就是错误的. 该小工具是基于androguard制作,本来是想自己写一个,后来一找居然有现成的可以利用就简单添加功能. 用法如下 dexdecompile.exe dexfilename outputdirectory 比如 dexdecompile.exe  c:\classes.dex c:\test 运行完毕以后将会在c:\test生成所需要的java源码. 下载地址:百度网盘地址

Java源码转C#源码的五款最佳工具

Java源码转C#源码的五款最佳工具 作者:chszs,转载需注明.博客主页:http://blog.csdn.net/chszs 出于某些需要,你可能会遇到把Java源码转换成C#源码的任务.如果是自己一边理解源码,再一边手工翻译,那效率肯定是很低的.有鉴于此,本文推荐了五款最佳的源码转换工具,以解决你的烦恼.工具1#:Java语言转换器助手地址:http://www.microsoft.com/en-us/download/details.aspx?id=14349 Java语言转换器助手是

Java源码阅读的真实体会

原文:http://zwchen.iteye.com/blog/1154193 刚才在论坛不经意间,看到有关源码阅读的帖子.回想自己前几年,阅读源码那种兴奋和成就感(1),不禁又有一种激动. 源码阅读,我觉得最核心有三点:技术基础+强烈的求知欲+耐心. 说到技术基础,我打个比方吧,如果你从来没有学过Java,或是任何一门编程语言如C++,一开始去啃<Core Java>,你是很难从中吸收到营养的,特别是<深入Java虚拟机>这类书,别人觉得好,未必适合现在的你. 虽然Tomcat的