zoukankan      html  css  js  c++  java
  • 顺序线性表 ---- ArrayList 源码解析及实现原理分析

    原创播客,如需转载请注明出处。原文地址:http://www.cnblogs.com/crawl/p/7738888.html 

    ----------------------------------------------------------------------------------------------------------------------------------------------------------

    笔记中提供了大量的代码示例,需要说明的是,大部分代码示例都是本人所敲代码并进行测试,源码已经标注出,不足之处,请大家指正~

    本博客中所有言论仅代表博主本人观点,若有疑惑或者需要本系列分享中的资料工具,敬请联系 qingqing_crawl@163.com

    -----------------------------------------------------------------------------------------------------------------------------------------------------------

    前言:

         前一段时间在大学课堂上学习了 c++ 版的数据结构,虽然考过了,但是感觉学的不扎实,不深入。尤其是楼主主要是 Java 方向的,所以一直想着再学习来一遍,一边学习数据结构,一边看着 jdk 源码,提升一下自己的内功。

         数据结构的开篇当然要拿顺序线性表开刀了。当然这一部分自认为还是比较简单,直接来拿 ArrayList 说事。

    一、ArrayList 概述

    ArrayList是 List 接口的可变数组的实现。除了实现 List 接口外,此类还提供一些方法来操作内部用来存储列表的数组的大小。

    每个 ArrayList 实例都有一个容量,该容量是指用来存储列表元素的数组的大小。随着向 ArrayList 中不断添加元素,其容量也自动增长自动增长会带来数据向新数组的重新拷贝,因此,如果可预知数据量的多少,可在构造 ArrayList 时指定其容量。

    二、ArrayList 源码解析

    值的注意的是,对于 ArrayList 来说,它实现了 List 接口,底层是使用数组来实现的,所以对 ArrayList 的操作,实际上就是对数组的操作。下面我们看一看,ArrayList 到底是如何实现的?

    1.底层使用数组实现:

    transient Object[] elementData;

    2. 构造方法:ArrayList 实现了三种形式的构造器,可以构造一个空的列表,也可以构造一个由我们指定初始容量的空列表,还可以构造一个包含 Collection

    的元素的非空列表

    源码:

    public ArrayList() {
            this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
        }
    
    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(Collection<? extends E> c) {
            elementData = c.toArray();
            if ((size = elementData.length) != 0) {
                // c.toArray might (incorrectly) not return Object[] (see 6260652)
                if (elementData.getClass() != Object[].class)
                    elementData = Arrays.copyOf(elementData, size, Object[].class);
            } else {
                // replace with empty array.
                this.elementData = EMPTY_ELEMENTDATA;
            }
        }

      3. set(int index, E element) 和 add(E e),这两个方法比较简单,简单说明一下即可。

    set(int index, E element) 可以用指定的元素替代列表中指定位置上的元素,并返回被替代了的元素,第二行的 rangeCheck() 方法是对传入的 index 进行范围的校验,很简单,不再说明。

    public E set(int index, E element) {
        rangeCheck(index);
    
        E oldValue = elementData(index);
        elementData[index] = element;
        return oldValue;
    }

    add(E e) 方法,将指定的元素添加到列表的末尾,第 2 行做的是检查添加后是否超过了数组的长度,如果超过了则为数组扩容,然后再添加。

    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }

    4. add(int index, E element) 将指定的元素插入到列表的指定位置

    解析:第 2 行的方法是对传入的 index 进行校验,判断其 index > size || index < 0,若满足此条件,则抛异常:IndexOutOfBoundsException(outOfBoundsMsg(index))。

    第 3 行的 ensureCapacityInternal() 方法是检查添加后是否超过了数组的长度,如果超过了则为数组扩容。

    第 5 行为主要操作,是将 elementData 数组中的从 index 开始,长度为  size - index 的元素拷贝到 index + 1 的位置上,即将这些元素后移一位。然后第 7 行将空缺出来的 index 位置上的元素赋值为出入的 element。

    1 public void add(int index, E element) {
    2         rangeCheckForAdd(index);
    3 
    4         ensureCapacityInternal(size + 1);  // Increments modCount!!
    5         System.arraycopy(elementData, index, elementData, index + 1,
    6                          size - index);
    7         elementData[index] = element;
    8         size++;
    9     }

    5. addAll(Collection<? extends E> c) 方法,将该 collection 中的所有元素添加到此列表的尾部,此方法不难理解,不再详解。

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

    6.addAll(int index, Collection<? extends E> c) :从指定的位置开始,将指定 collection 中的所有元素插入到此列表中。此方法与之前介绍的 add(int index, E element) 几乎一样,add(int index, E element) 方法已经详细介绍过了,也不再赘述。

     1 public boolean addAll(int index, Collection<? extends E> c) {
     2         rangeCheckForAdd(index);
     3 
     4         Object[] a = c.toArray();
     5         int numNew = a.length;
     6         ensureCapacityInternal(size + numNew);  // Increments modCount
     7 
     8         int numMoved = size - index;
     9         if (numMoved > 0)
    10             System.arraycopy(elementData, index, elementData, index + numNew,
    11                              numMoved);
    12 
    13         System.arraycopy(a, 0, elementData, index, numNew);
    14         size += numNew;
    15         return numNew != 0;
    16     }

    7. get(int index):返回此列表中指定位置上的元素。 同样先校验 index ,然后直接获取即可。

    1 public E get(int index) {
    2         rangeCheck(index);
    3 
    4         return elementData(index);
    5     }

    8. remove(int index):移除此列表中指定位置上的元素。

    解析:首先在第 2 行还是先校验传入 index 是否在正常的范围内。

    第 5 行取出 index 位置的元素。

    第 7 行定义了一个 munMoved = size - index - 1,对应为要删除的这个 index 位置的元素后面元素的个数若 index 位置后面还有元素,就将 elementData 从 index + 1 位置开始的 numMoved 个元素复制到 index 位置,即让 index 后面的元素向前移了一位,这样就将 index 位置的元素删除了。

     1 public E remove(int index) {
     2         rangeCheck(index);
     3 
     4         modCount++;
     5         E oldValue = elementData(index);
     6 
     7         int numMoved = size - index - 1;
     8         if (numMoved > 0)
     9             System.arraycopy(elementData, index+1, elementData, index,
    10                              numMoved);
    11         elementData[--size] = null; // clear to let GC do its work
    12 
    13         return oldValue;
    14     }

    为了让大家更好的理解,LZ 画了一个示意图供大家参考:

    9. remove(Object o):移除此列表中首次出现的指定元素(如果存在)。这是因为 ArrayList 中允许存放重复的元素。

    解析:值的注意的是 ArrayList 中允许存放 null 值,所以此操作要分两种情况来完成。

    而第 5 行和第 11 行的 fastRemove(int index) 方法,类似于 remove(int index) 方法,前面已经做了详细的讲解。 

     1 public boolean remove(Object o) {
     2         if (o == null) {
     3             for (int index = 0; index < size; index++)
     4                 if (elementData[index] == null) {
     5                     fastRemove(index);
     6                     return true;
     7                 }
     8         } else {
     9             for (int index = 0; index < size; index++)
    10                 if (o.equals(elementData[index])) {
    11                     fastRemove(index);
    12                     return true;
    13                 }
    14         }
    15         return false;
    16     }

    总结:关于 ArrayList 的源码解析和实现原理 LZ 先介绍到这里,都是挑选常用的方法来介绍的,欢迎大家一块学习,讨论交流。后续的关于数据结构的知识 LZ 还会持续更新~~

  • 相关阅读:
    unity3d工具栏介绍
    初识unity3d
    js模拟表单提交
    配置Appium环境
    html单选框 bootstrap模态框里面的单选框 和jq取值
    解决输入框总被浏览器记住的记录遮挡住的问题
    U盘
    python 字典列表/列表套字典 去重重复的字典数据
    ajax把数据return出去
    js把列表转换成字符串
  • 原文地址:https://www.cnblogs.com/crawl/p/7738888.html
Copyright © 2011-2022 走看看