ArrayList详解

    xiaoxiao2021-12-10  12

    ArrayList是我们使用的最常用的集合,下面我会从ArrayList特征和结构、源码的分析,以及自我实现ArrayList三个方面剖析ArrayList.

    ArrayList特征和结构

        ArrayList是java中的动态数组。它的容量能动态增长。它继承于AbstractList,实现了List, RandomAccess, Cloneable, java.io.Serializable这些接口。 ArrayList 继承了AbstractList,实现了List。它是一个数组队列,提供了相关的添加、删除、修改、遍历等功能。 ArrayList 实现了RandmoAccess接口,即提供了随机访问功能。RandmoAccess是java中用来被List实现,为List提供快速访问功能的。在ArrayList中,我们即可以通过元素的序号快速获取元素对象;这就是快速随机访问。稍后,我们会比较List的“快速随机访问”和“通过Iterator迭代器访问”的效率。 ArrayList 实现了Cloneable接口,即覆盖了函数clone(),能被克隆。 ArrayList 实现java.io.Serializable接口,这意味着ArrayList支持序列化,能通过序列化去传输。 和Vector不同,ArrayList中的操作不是线程安全的。所以,建议在单线程中才使用ArrayList,而在多线程中可以选择Vector或者CopyOnWriteArrayList。

    ArrayList源码分析

    我们看看ArrayList有哪些属性: /** * 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; /** * The size of the ArrayList (the number of elements it contains). * * @serial */ private int size; ArrayList中一切操作都是对elementData数组的操作,而size属性代表ArrayList中元素的个数。 我们看看ArrayList的基本的增、删、改、查操作: public boolean add(E e); public void add(int index, E element); public E set(int index, E element); public E remove(int index); public boolean remove(Object o); public int indexOf(Object o); 具体操作我就不写在上面了,我主要讨论一下里面涉及到的问题和原理.在add()操作过程中,会先判断当前数组容量是否足够插入数据. 扩充容量方法如下: 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); }     通过源码,我们可以看到, List集合的容量扩展是按以前容量的1.5倍增长的。add(index,e)的具体过程是怎么样的,它是数组的一种插入数据的操作.set(index,e)是将数组index下标的值设为e,它的返回值是原来index的旧值.这些基本的知识点,都需要我们通过查看ArrayList的源码去看的。别人说的再多作用也不大,当我们自己去查看ArrayList的源码时,就会有很多意想不到的收获的!

    数组的复制操作

        对ArrayList的操作就是对数组的操作,所以在ArrayList中有很多对数组的复制内容。接下来我们就一起来学习数组的复制. 数组的复制有两种方法: (1)使用System.arraycopy()复制 eg: int a[]={1,2,3,4,5,6,7}; int b[]=new int[5]; System.arraycopy(a,2,b,1,3); //参数的意义:a为原数组,2为a中开始复制的位置,b为目标数组,1为b中开始的位置,3为复制的长度 //所以b中的值为: 0,3,4,5,0 int a[]=new int[]{1,2,3,4,5,6,7}; int b[]=new int[2]; System.arraycopy(a,3,b,0,Math.min(a.length-3,b.length-0)); System.out.println(Arrays.toString(b)); src - 源数组。 srcPos - 源数组中的起始位置。 dest - 目标数组。 destPos - 目标数据中的起始位置。 length - 要复制的数组元素的数量。 (2)使用Arrays.copyOf()或Arrays.copyOfRange()方法 常用API如下: public static boolean[] copyOf(boolean[] original, int newLength); public static byte[] copyOf(byte[] original, int newLength); public static int[] copyOf(int[] original, int newLength); public static short[] copyOf(short[] original, int newLength); public static long[] copyOf(long[] original, int newLength); public static float[] copyOf(flot[] original, int newLength); public static double[] copyOf(double[] original, int newLength); public static T[] copyOf(T[] original, int newLength); public static T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType); 可以看到对基本数据类型都有对应的copyof方法,同时也有引用类型的复制 T[] copyOf,其内部都是调用System.arrayCopy()方法实现的.如果需要数组的复制范围选择,可以选择Arrays.copyOfRange()方法。 我们着重来看看 public static T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType); 方法。T,U肯定是两个不同的类型,那么又能实现从U数组的内容复制到T数组,那么能说明什么呢? 我们查看源码: public static T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) { 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; } 里面先通过 Array.newInstance(),反射创建一个与newType类型相同的数组,然后调用 system.arraycopy方法,实现数组的复制,那么能够从U类型数组复制元素到T类型元素,那么可以确定的是,T类型一定能兼容U类型,比如说向上转型,或者 Object o=new Integer(12) 我们通过例子说明: Integer[] intArray = new Integer[] { 1, 2, 3, 4, 5 }; Object obj2[] = copyOf(intArray, 4, Object[].class); for (Object obj : obj2) { System.out.println(obj); }

    ArrayList数组应用再探

    上面一部分都是数组工具类Arrays中常用的一些功能,其复制功能在ArrayList中得到了广泛的应用.接下来,我们再来看看ArrayList中的另外两个高级应用: (1)ArrayList转换成数组 相应API如下: public Object[] toArray(); public <t> T[] toArray(T[] a); </t>第一个方法没什么好说的,我们看第二个方法。传入T[]a,然后返回 T[]类型数组,这是个什么鬼! 还是看它的源代码吧: public <t> T[] toArray(T[] a) { if (a.length < size) // Make a new array of a's runtime type, but my contents: return (T[]) Arrays.copyOf(elementData, size, a.getClass()); System.arraycopy(elementData, 0, a, 0, size); if (a.length > size) a[size] = null; return a; } </t>分析结论如下:     当数组a的长度小于size,则创建新的数组,复制ArrayList中数组的内容并返回新数组;当数组a的长度等于size时,复制ArrayList中数组的内容到数组a中,并返回a;当数组a的长度大于size时,复制ArrayList中数组的内容到数组a中,设置a[size]=null,并返回a 大家有没有感觉一脸懵逼的感觉啊,为啥要这样设计咧!反正我感觉,直接传入T.class参数蛮好的: @SuppressWarnings("unchecked") public <t> T[] toArray(Class<? extends T> cls) { T newType[] = (T[]) Array.newInstance(cls, size); System.arraycopy(elementData, 0, newType, 0, size); return newType; } </t> (2)ArrayList的迭代器操作 在AbstractList中通过内部类实现Iterator接口,实现迭代,这里应用到了迭代器模式,大家可以通过查看源码来体会这种设计.以及实现ListIterator接口,实现List的前后双向迭代过程。

    自己实现ArrayList

    研究ArrayList源码,我们才能更清楚透彻的去了解ArrayList结构和实现。以前总是听别人说集合里面的代码特别的优秀,很值得我们阅读和借鉴。总是没时间,也不敢去尝试,终于花了一天时间来分析和了解ArrayList,并自己模仿写出自己的ArrayList,虽然有很多东西还是不知道为啥这样做,但是还是收获满满的,所以鼓励大家多看看源码,在这里我也无耻的粘上自己的源码咧! 自定义ArrayList下载 最后欢迎大家和我一起讨论学习,一起提高!
    转载请注明原文地址: https://ju.6miu.com/read-700419.html

    最新回复(0)