顺序线性表 ---- ArrayList 源码解析及实现原理分析
2017-10-28 14:10
579 查看
前言:
前一段时间在大学课堂上学习了 c++ 版的数据结构,虽然考过了,但是感觉学的不扎实,不深入。尤其是 LZ 主要是 Java 方向的,所以一直想着再学习来一遍,一边学习数据结构,一边看着 jdk 源码,提升一下自己的内功。
数据结构的开篇当然要拿顺序线性表开刀了。当然这一部分自认为还是比较简单,直接来拿 ArrayList 说事。
ArrayList是 List 接口的可变数组的实现。除了实现 List 接口外,此类还提供一些方法来操作内部用来存储列表的数组的大小。
每个 ArrayList 实例都有一个容量,该容量是指用来存储列表元素的数组的大小。随着向 ArrayList 中不断添加元素,其容量也自动增长。自动增长会带来数据向新数组的重新拷贝,因此,如果可预知数据量的多少,可在构造
ArrayList 时指定其容量。
值的注意的是,对于 ArrayList 来说,它实现了 List 接口,底层是使用数组来实现的,所以对 ArrayList 的操作,实际上就是对数组的操作。下面我们看一看,ArrayList 到底是如何实现的?
1.底层使用数组实现:
2. 构造方法:ArrayList 实现了三种形式的构造器,可以构造一个空的列表,也可以构造一个由我们指定初始容量的空列表,还可以构造一个包含 Collection
的元素的非空列表
源码:
3. set(int index, E element) 和 add(E e),这两个方法比较简单,简单说明一下即可。
set(int index, E element) 可以用指定的元素替代列表中指定位置上的元素,并返回被替代了的元素,第二行的 rangeCheck() 方法是对传入的 index 进行范围的校验,很简单,不再说明。
add(E e) 方法,将指定的元素添加到列表的末尾,第 2 行做的是检查添加后是否超过了数组的长度,如果超过了则为数组扩容,然后再添加。
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。
5. addAll(Collection<? extends E> c) 方法,将该 collection 中的所有元素添加到此列表的尾部,此方法不难理解,不再详解。
6.addAll(int index, Collection<? extends E> c) :从指定的位置开始,将指定 collection 中的所有元素插入到此列表中。此方法与之前介绍的 add(int
index, E element) 几乎一样,add(int index, E element) 方法已经详细介绍过了,也不再赘述。
7. get(int index):返回此列表中指定位置上的元素。 同样先校验 index ,然后直接获取即可。
8. remove(int index):移除此列表中指定位置上的元素。
解析:首先在第 2 行还是先校验传入 index 是否在正常的范围内。
第 5 行取出 index 位置的元素。
第 7 行定义了一个 munMoved = size - index - 1,对应为要删除的这个 index 位置的元素后面元素的个数,若 index 位置后面还有元素,就将 elementData 从 index + 1 位置开始的 numMoved 个元素复制到
index 位置,即让 index 后面的元素向前移了一位,这样就将 index 位置的元素删除了。
【本文由“程序员小子”发布,2017年10月28日】
前一段时间在大学课堂上学习了 c++ 版的数据结构,虽然考过了,但是感觉学的不扎实,不深入。尤其是 LZ 主要是 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(); i 4000 f ((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 位置的元素删除了。
【本文由“程序员小子”发布,2017年10月28日】
相关文章推荐
- java并发容器CopyOnWriteArrayList实现原理及源码分析
- 源码分析RocketMQ顺序消息消费实现原理
- CopyOnWriteArrayList实现原理及源码分析
- Java集合ArrayList实现原理——源码分析
- Java并发编程(五)ConcurrentHashMap的实现原理和源码分析
- ConcurrentLinkedQueue的实现原理和源码分析
- Spark MLlib LDA 基于GraphX实现原理及源码分析
- ARouter 依赖注入实现原理(源码解析)
- cvMorphology形态学原理解析及源码分析
- 【Android API】3.ViewPager的实现原理和源码分析
- HashSet实现原理及源码分析
- java自己实现顺序线性表ArrayList
- 集合之ArrayList实现源码分析
- HashMap实现原理及源码分析
- [置顶] [Java容器]HashMap实现原理和源码分析
- Java中HashMap底层实现原理(JDK1.8)源码分析
- HashMap实现原理及源码分析
- HashMap实现原理及源码分析
- Android 带你从源码的角度解析Scroller的滚动实现原理
- Java基于微信公众号接口实现授权登录源码及原理分析