Java ArrayList的模拟实现
Posted 再见孙悟空
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java ArrayList的模拟实现相关的知识,希望对你有一定的参考价值。
package test; import java.util.Arrays; import java.util.Collection; public class MyArrayList<E> { //存储数据 private Object[] values; //已经使用的容量 private int size; //修改次数 private int modCount; /** * 根据给定容量初始化 * @param initialCapacity */ public MyArrayList(int initialCapacity){ if(initialCapacity > 0){//按照给定的容量大小初始化数组 values = new Object[initialCapacity]; }else if(initialCapacity == 0){ values = new Object[]{}; }else{ throw new IllegalArgumentException("非法初始化容量: "+ initialCapacity); } } /** * 默认初始化方法 */ public MyArrayList(){ this(10); } /** * 用集合初始化一个ArrayList * @param c */ public MyArrayList(Collection c){ values = c.toArray(); if (values.length != 0) { size = values.length; if (values.getClass() != Object[].class) values = Arrays.copyOf(values, size, Object[].class); }else{ values = new Object[]{}; } } /** * 检查是否数组越界了 * @param index */ private void rangeCheck(int index) { if (index >= size || index < 0) throw new IndexOutOfBoundsException("数组越界了"); } /** * 替换原数组中的值,并且将原值返回 * @param index * @param element * @return */ public E set(int index,E element){ //检查是否越界 rangeCheck(index); E oldValue = (E)values[index]; values[index] = element; return oldValue; } /** * 在数组的末尾增加一个元素 * @param element */ public void add(E element){ //扩容 ensureCapacity(size+1); values[size++] = element; } /** * @param index * @param element */ public void add(int index,E element){ //先检查数组的下标是否越界了 rangeCheck(index); //扩容 ensureCapacity(size+1); Object[] oldValues = values; values[index] = element; //将原数组index后的值拷贝带新数组中 System.arraycopy(oldValues, index, values, index+1, size-index); size++; } /** * 将一个collection中的元素添加到数组末尾 * @param c */ public void addAll(Collection c){ Object[] cArray = c.toArray(); //扩容 ensureCapacity(size+cArray.length); //将传入数据拷贝到数组末尾 System.arraycopy(cArray, 0, values, size, cArray.length); size += cArray.length; } /** * 将一个collection中的元素添加到数组的指定位置 * @param index * @param c */ public void addAll(int index,Collection c){ //先检查数组的下标是否越界了 rangeCheck(index); //扩容 Object[] cArray = c.toArray(); int cLength = cArray.length; ensureCapacity(size+cLength); Object[] oldValues = values; //将传入数据拷贝到数组指定位置 System.arraycopy(cArray, 0, values, index, cLength); //将原数组中index以后的数据拷贝过来 System.arraycopy(oldValues, index, values, values.length-1, size-index); size += cLength; } /** * 根据下标获取元素 * @param index * @return */ public E get(int index){ //先检查数组的下标是否越界了 rangeCheck(index); E e = (E) values[index]; return e; } /** * 根据下标删除元素 * @param index * @return */ public E remove(int index){ //先检查数组的下标是否越界了 rangeCheck(index); E removeValue = (E) values[index]; System.arraycopy(values, index+1, values, index, size-index-1); //置为空,gc回收 values[size--] = null; return removeValue; } /** * 根据对象删除 * @param o */ public void remove(Object o){ //考虑元素为null的情况 if(null == o){ for (int i = 0; i < size; i++) { if(values[i] == null){ remove(i); } } }else{ for (int i = 0; i < size; i++) { if(values[i].equals(o)){ remove(i); } } } } /** * 扩容方法,保证数组的长度足够 * @param minCapacity */ public void ensureCapacity(int minCapacity){ modCount++; int oldCapacity = values.length; if (minCapacity > oldCapacity) { //先默认扩容50%+1,如果还是不能满足,则直接用传入的所需容量 int newCapacity = (oldCapacity*3)/2+1; if(newCapacity < minCapacity){ newCapacity = minCapacity; } values = Arrays.copyOf(values, newCapacity); } } public int size(){ return size; } }
最后再介绍一下ensureCapacity这个方法的想法(从网上看到的):
我们知道ArrayList的内部是采用数组来存储元素的,由于java数组都是定长的,所以这个数组的大小一定是固定的,这个大小就是capacity。我们可以肯定capacity一定是大于或等于ArrayList的size,那么当size不断增加到了要超过capacity的时候,ArrayList就不得不重新创建新的capacity来容纳更多的元素,这时需要首先建立一个更长的数组,将原来的数组中的元素复制到新数组中,再删除原来的数组。可见当ArrayList越来越大时,这种操作的消耗也是越来越大的。
为了减少这种不必要的重建capacity的操作,当我们能肯定ArrayList大致有多大(或者至少会有多大)时,我们可以先让ArrayList把capacity设为我们期望的大小,以避免多余的数组重建。
假设ArrayList自动把capacity设为10,每次重建时将长度递增原来的三分之二,那么当我们需要大约存储50个元素到ArrayList中时,就会大约需要重建数组4次,分别是在增加第11、第17、第26、第39个元素的时候进行的。如果我们一开始就让ArrayList的capacity为50,那么不需要任何数组重建就能完成所有插入操作了。
java允许我们在构造ArrayList的同时指定capacity,如new ArrayList(50),也允许在以后将它设得更大,而增大capacity就是使用ensureCapacity()方法。注意:capacity只能比原来的更大,而不能比原来的更小,否则java会忽略该操作。ArrayList的初始默认capacity为10,所以给capacity指定小于10的整数是毫无意义的。
最后说说ArrayList的size,前面说过,size一定小于等于capactiy,而且更重要的是,访问超过size的位置将抛出异常,尽管这个位置可能没有超过capacity。ensureCapacity()只可能增加capacity,而不会对size有任何影响。要增加size,只能用add()方法。
以上是关于Java ArrayList的模拟实现的主要内容,如果未能解决你的问题,请参考以下文章
[Java]ArrayList与LinkedList的模拟实现