通过一边查阅网上的文章一边根据JDK1.8中的Arraylist的源码自己动手一步步实现一个Arraylist例子
声明变量
private static final int DEFAULT_CAPACITY = 10;//数组需要扩容时与传入的指定长度比较取较大的一方作为扩容的目标容量 private int size;//记录数组里不为空的数据的长度 transient Object[] elementData;//存放数据的数组对象 private static final Object[] EMPTY_ELEMENTDATA = {};//空数组 private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};//空数组,无参数构造方法时给elementData初始赋值 private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;//数组容量上限
三个构造方法:
public MyArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;//无参数构造方法,用空数组赋值 }
public MyArrayList(int initialCapacity) { //带int参数定义elementData初始长度,一个List长度确定时推荐调用,可以免去add方法内的数组扩容,节省性能。 if (initialCapacity > 0) { this.elementData = new Object[initialCapacity]; } else if (initialCapacity == 0) { this.elementData = EMPTY_ELEMENTDATA;//指定容量为0,用空数组赋值,跟无参数构造非同一个(暂未理解用处) } else { throw new IllegalArgumentException("Illegal Capacity: " + initialCapacity); } }
public MyArrayList(Collection extends E> c) { //将传入的Collection转化为数组 elementData = c.toArray(); if ((size = elementData.length) != 0) { // 参数为非空集合 if (elementData.getClass() != Object[].class) // 是否成功转化为Object类型数组 elementData = Arrays.copyOf(elementData, size, Object[].class); // 不为Object数组的话就进行复制 } else { this.elementData = EMPTY_ELEMENTDATA;// 集合大小为空,则设置元素数组为空 } }
第三个构造方法中出现c.toArray()和Arrays.copyOf(elementData,size,Object[].class)方法,不去看他们的实现总觉得不踏实并不能很好的记忆ArrayList的实现,于是去看了他们的实现,下面进行讲解。
toArray() 这是Collection接口内定义的方法:
Object[] toArray();
看命名就知道它的作用是返回一个数组,我们熟知的List除了有ArrayList之外还有一个LinkedList,因为这两个类内部存储数据方式不一样,toArray的具体实现也不一样这里贴出两个类各自的toArray的实现代码做一个对比。
LinkedList:
public Object[] toArray() { Object[] result = new Object[size]; int i = 0; for (Node<E> x = first; x != null; x = x.next) result[i++] = x.item; return result; }
LinkedList内部存储的方式是链式储存,这里通过for循环将储存的数据转换成数组。
ArrayList:
public Object[] toArray() { return Arrays.copyOf(elementData, size); }
又出现了Arrays.copyOf()方法,接下来只要了解下Arrays方法就好了。
Arrays.copy:
public static <T> T[] copyOf(T[] original, int newLength) { return (T[]) copyOf(original, newLength, original.getClass()); }
public static <T,U> T[] copyOf(U[] original, int newLength, Class extends T[]> newType) { @SuppressWarnings("unchecked") T[] copy = ((Object)newType == (Object)Object[].class) ? (T[]) new Object[newLength] : (T[]) Array.newInstance(newType.getComponentType(), newLength); System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength)); return copy; }
这里用class对象作为判断,我java底子比较薄弱,以前不知道XX[].class和XX.class的区别,所以学习了解了下后知道所有对象都是继承自Object的都有自己的一个class,那么xx[]对象自然也是继承自Object并拥有一个class,xx[].class不同于xx.class。
(Object)newType == (Object)Object[].class 这一个判断传入的original源数组是否为Object[]数组,是的话直接new一个容量为newLength的Object[]对象,否则进入Array.newInstance(),这里自然是要进入这个方法一探究竟。
Array.newInstance():
public static Object newInstance(Class> componentType, int length) throws NegativeArraySizeException { return newArray(componentType, length); }
private static Object newArray(Class> componentType, int size) throws NegativeArraySizeException { if (!componentType.isPrimitive()) { return createObjectArray(componentType, size); } else if (componentType == char.class) { return new char[size]; } else if (componentType == int.class) { return new int[size]; } else if (componentType == byte.class) { return new byte[size]; } else if (componentType == boolean.class) { return new boolean[size]; } else if (componentType == short.class) { return new short[size]; } else if (componentType == long.class) { return new long[size]; } else if (componentType == float.class) { return new float[size]; } else if (componentType == double.class) { return new double[size]; } else if (componentType == void.class) { throw new IllegalArgumentException("Can't allocate an array of void"); } throw new AssertionError(); }
进去一看,根据newType.getComponentType()传入的class进行判断返回数组的类型,在这里我又生出一个疑问,为什么同样是class,前面是用original.getClass()方法,而这里的Array.newInstance()用的却是newType.getComponentType()方法。于是去查了newType.getComponentType()的作用,这是一个数组对象才会有返回值的方法,正如上面所写的xx[].class不等于xx.class,Array.newInstance()方法是要新建一个储存xx类型的数组,需要的是xx.class,而newType.getComponentType()方法就是返回xx.class,具体例子 我们需要从String[]类型的a对象的身上获取String.class,就要a.getClass().getComponentType()这样写。 到这里为止只需要再了解一下 System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength));这个方法是干嘛的就能彻底了解ArrayList的构造方法干了什么。
System.arraycopy很简单,参数分别为(original=源数组,0=源数组起始位,copy=数据填充的数组对象,0=数据填充的数组对象起始位,Math.min(original.length, newLength)=拷贝长度)
比较抽象,还是看几个简单的例子比较实在:
int newLength = 4; int original[] = {1, 2, 3, 4, 5}; int copy[] = new int[newLength]; System.arraycopy(original, 0, copy, 0, 4); Log.d("", Arrays.toString(copy));
输出copy对象的结果为:[1, 2, 3, 4]
int newLength = 4; int original[] = {1, 2, 3, 4, 5}; int copy[] = new int[newLength]; System.arraycopy(original, 1, copy, 0, 4); Log.d("", Arrays.toString(copy));
输出copy对象的结果为:[2, 3, 4, 5]
int newLength = 4; int original[] = {1, 2, 3, 4, 5}; int copy[] = new int[newLength]; System.arraycopy(original, 0, copy, 1,3); Log.d("", Arrays.toString(copy));
输出copy对象的结果为:[0, 1, 2, 3],这里因为copy后面的数值为1表示从copy对象下标为1的地方开始填充,所以下标为0的数值是int对象默认的0。
以上就是我对ArrayList构造方法的理解,写成文章作为记录。
主要参考文章:https://www.cnblogs.com/leesf456/p/5308358.html 讲的非常清晰易懂,配有图文,理解起来很轻松。