Java记录 -46- ArrayList源码剖析
2015-10-27 23:30
453 查看
在编程中经常会使用到集合,而集合中最常用的是ArrayList。
当我们学习了数组又学习集合时,发现集合很神奇。数组需要在定义的时候声明大小,而ArrayList不用管大小,定义了以后可以随便使用。
查看ArrayList的源代码,可以发现,ArrayList底层是用数组进行存放元素的。ArrayList并不神奇,底层实现是通过一个对象数组Object[]来存放元素,由于是对象数组,所以只能存放对象,并可以存放任何对象。而数组存放了对象的引用,所以ArrayList也存放对象的引用。
ArrayList的两个构造函数:
我们通常使用ArrayList时会使用其不带参数的构造函数,或指定一个容量的参数的构造函数,而其底层实现是不带参数的构造函数会调用带参数的构造函数,只是不带参数的构造函数会默认使用传递一个10参数,带参数的构造函数中会声明一个长度为10的Object数组。
下面是我们经常使用的add方法,该方法首先会判断底层数组的长度是否大于需要的长度,让将元素放入底层数组中。
ArraList中一个很重要的方法(ArrayList底层是使用数组实现的,为什么它可以无限添加元素呢?)
下面这个方法ensureCapacity做了实现,minCapacity是要追加元素的位置,它和底层数组的大小做比较,如果小于底层数组的长度则什么也不做;重点在大于底层数组长度时,它做了一些事情:将原数组的大小进行扩容,扩多大呢,原数组的3/2+1;然后使用Arrays.copyOf方法,将原数组的元素拷贝到新指定大小的数组里,至此实现数组的扩容。
首先也是判断删除的元素索引是否越界,要删除的元素的索引如果大于底层的数组大小则抛出异常;
如果小于则先从底层数组中获取该元素的值,主要用于remove方法返回使用;
然后判断要删除的元素位置是否为底层数组的最后一个元素,如果是则将底层数组的最后一个元素置null,便于垃圾回收;
如果不是最后一个元素,则利用System的arraycopy方法将要删除的元素后面的每个元素都前移一个位置。
该方法首先会判断是否需要扩容,然后再将要插入的位置以及其后面的元素后移一个位置,为插入的元素腾地。
ArrayList的插入和删除需要付出相当高的代价,都需要将元素进行移位。
对ArrayList的理解,主要要知道的是其底层的实现是使用一个数组来存储元素。其他的方法都是基于该数组在进行操作。
在此可以体会到数组的基础是多么的重要。
集合中存放的依然是对象的引用,而不是对象本身,和数组一样。集合中无法放置原生数据类型,我们需要使用原生数据类型的包装类才能放入到集合中。
集合中放置的是Object类型,因此取出来的也是Object类型,那么必须使用强制类型转换将其转换为真正类型,即放入的类型。
当我们学习了数组又学习集合时,发现集合很神奇。数组需要在定义的时候声明大小,而ArrayList不用管大小,定义了以后可以随便使用。
查看ArrayList的源代码,可以发现,ArrayList底层是用数组进行存放元素的。ArrayList并不神奇,底层实现是通过一个对象数组Object[]来存放元素,由于是对象数组,所以只能存放对象,并可以存放任何对象。而数组存放了对象的引用,所以ArrayList也存放对象的引用。
ArrayList的两个构造函数:
我们通常使用ArrayList时会使用其不带参数的构造函数,或指定一个容量的参数的构造函数,而其底层实现是不带参数的构造函数会调用带参数的构造函数,只是不带参数的构造函数会默认使用传递一个10参数,带参数的构造函数中会声明一个长度为10的Object数组。
/** * The array buffer into which the elements of the ArrayList are stored. * The capacity of the ArrayList is the length of this array buffer. */ private transient Object[] elementData; public ArrayList(int initialCapacity) { super(); if (initialCapacity < 0) throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity); this.elementData = new Object[initialCapacity]; } /** * Constructs an empty list with an initial capacity of ten. */ public ArrayList() { this(10); }
下面是我们经常使用的add方法,该方法首先会判断底层数组的长度是否大于需要的长度,让将元素放入底层数组中。
/** * Appends the specified element to the end of this list. * * @param e element to be appended to this list * @return <tt>true</tt> (as specified by {@link Collection#add}) */ public boolean add(E e) { ensureCapacity(size + 1); // Increments modCount!! elementData[size++] = e; return true; }ArrayList的size()方法返回的是集合的大小,其实是底层数组的长度。
ArraList中一个很重要的方法(ArrayList底层是使用数组实现的,为什么它可以无限添加元素呢?)
下面这个方法ensureCapacity做了实现,minCapacity是要追加元素的位置,它和底层数组的大小做比较,如果小于底层数组的长度则什么也不做;重点在大于底层数组长度时,它做了一些事情:将原数组的大小进行扩容,扩多大呢,原数组的3/2+1;然后使用Arrays.copyOf方法,将原数组的元素拷贝到新指定大小的数组里,至此实现数组的扩容。
/** * Increases the capacity of this <tt>ArrayList</tt> instance, if * necessary, to ensure that it can hold at least the number of elements * specified by the minimum capacity argument. * * @param minCapacity the desired minimum capacity */ public void ensureCapacity(int minCapacity) { modCount++; int oldCapacity = elementData.length; if (minCapacity > oldCapacity) { Object oldData[] = elementData; int newCapacity = (oldCapacity * 3)/2 + 1; if (newCapacity < minCapacity) newCapacity = minCapacity; // minCapacity is usually close to size, so this is a win: elementData = Arrays.copyOf(elementData, newCapacity); } }我们经常使用的获取集合元素的方法get(),首先会检查要获取的元素索引是否大于底层数组的大小,如果大于则抛出索引越界异常;否则就返回数组指定位置的元素。相对来说ArrayList的方法很简单,就是返回底层数组中指定位置的元素。
/** * Returns the element at the specified position in this list. * * @param index index of the element to return * @return the element at the specified position in this list * @throws IndexOutOfBoundsException {@inheritDoc} */ public E get(int index) { RangeCheck(index); return (E) elementData[index]; } /** * Checks if the given index is in range. If not, throws an appropriate * runtime exception. This method does *not* check if the index is * negative: It is always used immediately prior to an array access, * which throws an ArrayIndexOutOfBoundsException if index is negative. */ private void RangeCheck(int index) { if (index >= size) throw new IndexOutOfBoundsException("Index: "+index+", Size: "+size); }下面是ArrayList的remove方法实现,
首先也是判断删除的元素索引是否越界,要删除的元素的索引如果大于底层的数组大小则抛出异常;
如果小于则先从底层数组中获取该元素的值,主要用于remove方法返回使用;
然后判断要删除的元素位置是否为底层数组的最后一个元素,如果是则将底层数组的最后一个元素置null,便于垃圾回收;
如果不是最后一个元素,则利用System的arraycopy方法将要删除的元素后面的每个元素都前移一个位置。
/** * Removes the element at the specified position in this list. * Shifts any subsequent elements to the left (subtracts one from their * indices). * * @param index the index of the element to be removed * @return the element that was removed from the list * @throws IndexOutOfBoundsException {@inheritDoc} */ public E remove(int index) { RangeCheck(index); modCount++; E oldValue = (E) elementData[index]; int numMoved = size - index - 1; if (numMoved > 0) System.arraycopy(elementData, index+1, elementData, index, numMoved); elementData[--size] = null; // Let gc do its work return oldValue; }ArrayList有一个在指定位置插入元素的操作,public void add(int index, E element);
该方法首先会判断是否需要扩容,然后再将要插入的位置以及其后面的元素后移一个位置,为插入的元素腾地。
ArrayList的插入和删除需要付出相当高的代价,都需要将元素进行移位。
对ArrayList的理解,主要要知道的是其底层的实现是使用一个数组来存储元素。其他的方法都是基于该数组在进行操作。
在此可以体会到数组的基础是多么的重要。
集合中存放的依然是对象的引用,而不是对象本身,和数组一样。集合中无法放置原生数据类型,我们需要使用原生数据类型的包装类才能放入到集合中。
集合中放置的是Object类型,因此取出来的也是Object类型,那么必须使用强制类型转换将其转换为真正类型,即放入的类型。
相关文章推荐
- 从头认识java-4.7 构造器初始化(3)
- 从头认识java-4.7 构造器初始化(3)
- spring事务(Transaction )报 marked as rollback-only异常的原因及解决方法
- Java:泛型
- Spring2:bean的使用
- JAVA设计模式之单例模式
- Java包装类学习笔记(1)
- Eclipse环境快速搭载
- 从头认识java-4.7 构造器初始化(2)
- Java:静态导入
- 从头认识java-4.7 构造器初始化(2)
- Java-Spring-WebService最基础的配置示例
- Java-Spring-WebService最基础的配置示例
- Java-Spring-WebService最基础的配置示例
- java内存分配和String类型的深度解析
- Java:集合for高级循环遍历
- spring MVC项目中,欢迎页首页根路径到底是怎么设置的
- Java:集合框架的工具类
- java代码安全性
- java序列化