原创播客,如需转载请注明出处。原文地址: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。

 public void add(int index, E element) {
rangeCheckForAdd(index); ensureCapacityInternal(size + 1); // Increments modCount!!
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}

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) 方法已经详细介绍过了,也不再赘述。

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

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

 public E get(int index) {
rangeCheck(index); return elementData(index);
}

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

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

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

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

 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; // clear to let GC do its work return oldValue;
}

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

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

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

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

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

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

顺序线性表 ---- ArrayList 源码解析及实现原理分析的更多相关文章

  1. 面试必备:ArrayList源码解析(JDK8)

    面试必备:ArrayList源码解析(JDK8) https://blog.csdn.net/zxt0601/article/details/77281231 概述很久没有写博客了,准确的说17年以来 ...

  2. Java中的容器(集合)之ArrayList源码解析

    1.ArrayList源码解析 源码解析: 如下源码来自JDK8(如需查看ArrayList扩容源码解析请跳转至<Java中的容器(集合)>第十条):. package java.util ...

  3. ArrayList源码解析--值得深读

    ArrayList源码解析 基于jdk1.8 ArrayList的定义 类注释 允许put null值,会自动扩容: size isEmpty.get.set.add等方法时间复杂度是O(1): 是非 ...

  4. ArrayList源码解析

    ArrayList简介 ArrayList定义 1 public class ArrayList<E> extends AbstractList<E> implements L ...

  5. ArrayList源码解析(二)

    欢迎转载,转载烦请注明出处,谢谢. https://www.cnblogs.com/sx-wuyj/p/11177257.html 自己学习ArrayList源码的一些心得记录. 继续上一篇,Arra ...

  6. Collection集合重难点梳理,增强for注意事项和三种遍历的应用场景,栈和队列特点,数组和链表特点,ArrayList源码解析, LinkedList-源码解析

    重难点梳理 使用到的新单词: 1.collection[kəˈlekʃn] 聚集 2.empty[ˈempti] 空的 3.clear[klɪə(r)] 清除 4.iterator 迭代器 学习目标: ...

  7. 【源码解析】- ArrayList源码解析,绝对详细

    ArrayList源码解析 简介 ArrayList是Java集合框架中非常常用的一种数据结构.继承自AbstractList,实现了List接口.底层基于数组来实现动态容量大小的控制,允许null值 ...

  8. 老李推荐:第6章4节《MonkeyRunner源码剖析》Monkey原理分析-事件源-事件源概览-翻译命令字串

    老李推荐:第6章4节<MonkeyRunner源码剖析>Monkey原理分析-事件源-事件源概览-翻译命令字串   poptest是国内唯一一家培养测试开发工程师的培训机构,以学员能胜任自 ...

  9. 老李推荐:第6章5节《MonkeyRunner源码剖析》Monkey原理分析-事件源-事件源概览-事件

    老李推荐:第6章5节<MonkeyRunner源码剖析>Monkey原理分析-事件源-事件源概览-事件   从网络过来的命令字串需要解析翻译出来,有些命令会在翻译好后直接执行然后返回,但有 ...

随机推荐

  1. 多线程面试题系列(8):经典线程同步 信号量Semaphore

    前面介绍了关键段CS.事件Event.互斥量Mutex在经典线程同步问题中的使用.本篇介绍用信号量Semaphore来解决这个问题. 首先也来看看如何使用信号量,信号量Semaphore常用有三个函数 ...

  2. java.lang.IllegalArgumentException: object is not an instance of declaring class

    今天在使用反射的时候,出现了java.lang.IllegalArgumentException: object is not an instance of declaring class错误-具体是 ...

  3. php-laravel4.0框架 简单快速入门

    前提必须已经安装好了laravel4.0版本. 写入权限: 安装完 Laravel ,你还需要为web服务器设置 app/storage 目录的写入权限. 目录结构: 安装完框架后,你需要熟悉一下该项 ...

  4. JavaScript中的位置屬性

    屏幕中的位置(直接使用,無需前綴): screenLeft.screenTop:除了火狐都支持 screenX.screenY: 窗口的大小(谷歌的inner=outer,直接使用,無需前綴): in ...

  5. ACM学习之路___HDU 5723(kruskal + dfs)

    Abandoned country Time Limit: / MS (Java/Others) Memory Limit: / K (Java/Others) Total Submission(s) ...

  6. Vue.js 服务端渲染业务入门实践

    作者:威威(沪江前端开发工程师) 本文原创,转载请注明作者及出处. 背景 最近, 产品同学一如往常笑嘻嘻的递来需求文档, 纵使内心万般拒绝, 身体倒是很诚实. 接过需求,好在需求不复杂, 简单构思 后 ...

  7. oracle pl/sql 包

    包用于在逻辑上组合过程和函数,它由包规范和包体两部分组成.1).我们可以使用create package命令来创建包,如:i.创建一个包sp_packageii.声明该包有一个过程update_sal ...

  8. C++拷贝构造函数专题

    C++拷贝构造函数 在定义任何C++类时,拷贝空置操作都是必要部分,类需要控制拷贝.赋值和销毁对象时发生的行为.拷贝构造函数的用途:当创建了一个新对象时,可以用另一个对象的数据初始化这个新建的对象. ...

  9. Linux crontab任务调度

    一.crontab说明 Linux crontab任务调度是在规定的时间频率内去执行相应的任务. 二.crontab文件详情 1.crontab文件在Linux中的/etc/crontab 2.查看c ...

  10. Python in/not in --- if not/if + for...[if]...构建List+ python的else子句

    区分几个容易出错的地方: in 成员运算符 - 如果字符串中包含给定的字符返回 True >>>"H" in a True not in 成员运算符 - 如果字符 ...