ArrayList解析

public class ArrayList<E> extends AbstractList<E>implements List<E>, RandomAccess, Cloneable, java.io.Serializable

       private static final long serialVersionUID = 8683452581122892189L;

//序列号

注:关于序列化

对象的序列化主要有两种用途:

1) 把对象的字节序列永久地保存到硬盘上,通常存放在一个文件中;

2) 在网络上传送对象的字节序列。

ObjectOutputStream:代表对象输出流,它的writeObject(Object obj)方法可对参数指定的obj对象进行序列化,把得到的字节序列写到一个目标输出流中。

ObjectInputStream:代表对象输入流,它的readObject()方法从一个源输入流中读取字节序列,再把它们反序列化为一个对象,并将其返回。

只有实现了Serializable和Externalizable接口的类的对象才能被序列化。Externalizable接口继承自 Serializable接口,实现Externalizable接口的类完全由自身来控制序列化的行为,而仅实现Serializable接口的类可以 采用默认的序列化方式 。

      private static final int DEFAULT_CAPACITY = 10;


     private static final Object[] EMPTY_ELEMENTDATA = {};

//指定为0时返回的空数组

     private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

//默认返回的空数组

      transient Object[] elementData


      private int size;

--------------------------------------------------------------------------------------------------------

关于它的构造函数:

public ArrayList(int initialCapacity) {

//传入一个整型的首容量.

        if (initialCapacity > 0) {

//首容量>0则建立一个首容量大小的object数组

            this.elementData = new Object[initialCapacity];

        } else if (initialCapacity == 0) {

//首容量==0,建立一个EMPTY_ELEMENTDATA = {}的空数组

            this.elementData = EMPTY_ELEMENTDATA;

        } else {

//要是首容量<0,抛出异常

            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }

    }

----------------------------------------------------------------------------------------------------------

 public ArrayList() {

//生成一个临时的object数组并赋空。

        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;

    }

-----------------------------------------------------------------------------------------------------------

注:关于class和getclass的区别

java有两个获得类名的方法getClass()和class(),这两个方法看似一样,实则不然。这两个方法涉及到了java中的反射。

反射:在运行时期获取对象类型信息的操作。(在反射的帮助下,编程人员可以动态获取这些信息,从而编写更加具有可移植性的代码)

类型类 :在Java中一切都是对象,一般所使用的对象都直接或间接继承自Object类。Object类中包含一个方法名叫getClass,利用这个方法就可以获得一个实例的类型类。类型类指的是代表一个类型的类,因为一切皆是对象,类型也不例外,在Java使用类型类来表示一个类型。所有的类型类都是Class类的实例。

getClass()是一个类的实例所具备的方法,而class()方法是一个类的方法。 另外getClass()是在运行时才确定的,而class()方法是在编译时就确定了。

 public ArrayList(Collection<? extends E> c) {

//传入一个collection集合,将集合转化为一个临时的数组。

       elementData = c.toArray();

        if ((size = elementData.length) != 0) {

//给size赋值,并判断size不为0

            // c.toArray might (incorrectly) not return Object[] (see 6260652)

            //toArray方法不一定返回一个object数组

            if (elementData.getClass() != Object[].class)

//判断c.toArray()是否返回了一个object类型的数组,如果没有,用 public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType)方法得到一个object的数组。

                elementData = Arrays.copyOf(elementData, size, Object[].class);

        } else {

            // replace with empty array.如果size为0,就直接返回一个空的object的数组
            this.elementData = EMPTY_ELEMENTDATA;
        }

    }

----------------------------------------------------------------------------------------------------

其他方法

    将数组缓冲区大小调整到实际 ArrayList 存储元素的大小,即 elementData = Arrays.copyOf(elementData, size); 
该方法由用户手动调用,以减少空间资源浪费的目的 
   public void trimToSize() {

        modCount++;

 // modCount 是 AbstractList 的属性值:protected transient int modCount = 0;             

 // 将“修改统计数”+1,该变量主要是用来实现fail-fast机制的              

 // 当实际大小 < 数组缓冲区大小时  

 // 如调用默认构造函数后,刚添加一个元素,此时 elementData.length = 10,而 size = 1  

 // 通过这一步,可以使得空间得到有效利用,而不会出现资源浪费的情况  
        if (size < elementData.length) {

            elementData = (size == 0)? EMPTY_ELEMENTDATA : Arrays.copyOf(elementData, size);

//如果size==0成立就付给elementData一个空数组,不成立就给elementData赋一个size大小的数组,由此调整数组的缓冲区大小

        }

    }

---------------------------------------------------------------------------------------------------------

指定 ArrayList 的容量 
//minCapacity   指定的最小容量 

  public void ensureCapacity(int minCapacity) {
        int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)

         ? 0    : DEFAULT_CAPACITY;

//传入一个最小容量,判断elementData是否为空数组,是最小扩容为0,否则为10

        if (minCapacity > minExpand) {

//如果最小容量大于最小扩容执行此方法

            ensureExplicitCapacity(minCapacity);
        }

    }

----------------------------------------------------------------------------------------------------

用于计算容量

 private static int calculateCapacity(Object[] elementData, int minCapacity) {

        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {

//如果elementData数组为空

            return Math.max(DEFAULT_CAPACITY, minCapacity);

//判断10和传入的最小容量,并返回最大值

        }

//数组不为空,就返回最小容量

        return minCapacity;

        }

--------------------------------------------------------------------------------------------------

明确 ArrarList 的容量,提供给本类使用的方法 , 用于内部优化,保证空间资源不被浪费:尤其在 add() 方法添加时起效
    private void ensureCapacityInternal(int minCapacity) {

        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));

    }

---------------------------------------------------------------------------------------------------


私有方法:明确 ArrayList 的容量 

用于内部优化,保证空间资源不被浪费:尤其在 add() 方法添加时起效 


    private void ensureExplicitCapacity(int minCapacity) {

//传入最小容量

        modCount++;

       // 将“修改统计数”+1,该变量主要是用来实现fail-fast机制的   

       // overflow-conscious code( 防止溢出代码:确保指定的最小容量 > 数组缓冲区当前的长度)

        if (minCapacity - elementData.length > 0)

//最小容量大于elementData数组的长度,就用grow()扩容

            grow(minCapacity);

    }

-----------------------------------------------------------------------------------------------------------

 private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

//public static final int   MAX_VALUE = 0x7fffffff;(

-----------------------------------------------------------------------------------------------------------

扩容:

注:关于移位:(将数值化为二进制,向左或向右移规定的位数,然而odlcapacity>>1就相当于移了0.5倍

      数组缓冲区最大存储容量:

          一些 VM 会在一个数组中存储某些数据。要减去 8 的原因 : 尝试分配这个最大存储容量,可能会导致 OutOfMemoryError(当该值 > VM 的限制时) 。

 private void grow(int minCapacity) {

        // overflow-conscious code

//传入一个最小容量

        int oldCapacity = elementData.length;

//一个旧容量

        int newCapacity = oldCapacity + (oldCapacity >> 1);

//新容量是旧容量的1.5倍

        if (newCapacity - minCapacity < 0)

            newCapacity = minCapacity;

//新容量等于指定容量和旧容量中更大的一方

        if (newCapacity - MAX_ARRAY_SIZE > 0)

//如果新容量大于一个int型所能表示的最大数值-8,就调用hugeCapacity()方法

            newCapacity = hugeCapacity(minCapacity);

        // minCapacity is usually close to size, so this is a win:

//如果可以就直接生成一个新容量的object数组

        elementData = Arrays.copyOf(elementData, newCapacity);

    }

------------------------------------------------------------------------------------------------------------

大容量分配,最大分配 Integer.MAX_VALUE 
    private static int hugeCapacity(int minCapacity) {

        if (minCapacity < 0) // overflow

//传入的最小扩容<0,就抛出异常

            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :

            MAX_ARRAY_SIZE;

//如果最小容量大于一个int型所能表示的最大数值-8,返回一个int型能表示的最大值,否则返回一个int型所能表示的最大数值-8

    }

-------------------------------------------------------------------------------------------------------------

关于输入最小扩容,进行扩容操作的逻辑总结:

进行自己设置的最小容量 的扩容操作,要用public void ensureCapacity(int minCapacity)方法(其他的都是私有方法,用来完善你的扩容操作),首先传入一个最小扩容量,开始判断你设置的ArrayList的对象是否是默认的空数组,是则说明当前分配容量为0,否则表面当前分配容量为10,如果你设置的最小容量小于0或10,就去调用 private void ensureExplicitCapacity(int minCapacity) 来进行扩容操作,要是满足最小容量大于当前数组长度,就进行扩容,判断最小扩容和自动扩容的大小,可能是以1.5倍增加,也可以是直接以最小扩容增加,(最大扩容:存在生成一个int型的最大扩容)生成一个扩容后的object数组。

-------------------------------------------------------------------------------------------------------------

获取该 list 所实际存储的元素个数 

 public int size() {
        return size;

    }

------------------------------------------------------------------------------------------------------------

判断 list 是否为空 

 public boolean isEmpty() {

        return size == 0;

    }

-------------------------------------------------------------------------------------------------------------

判断是否存在一个object的对象

    public boolean contains(Object o) {

//传回来的值如果是>=0就证明找到了相应的下标,否则,没有找到

        return indexOf(o) >= 0;

    }

--------------------------------------------------------------------------------------------------------------

顺序查找,返回元素的最低索引值(最首先出现的索引位置) 

    public int indexOf(Object o) {

        if (o == null) {

//o为空,就去找集合里找一个null元素,并返回下标位置(注意:元素为 null 并非表示这是非法操作。空值也可以作为元素放入 ArrayList)

            for (int i = 0; i < size; i++)

//顺序查找数组缓冲区。注意:Arrays 工具类提供了二分搜索,但没有提供顺序查找  

                if (elementData[i]==null)
                    return i;

        } else {

//o不为空就通过循环,找和o内容相等的元素,并返回下标

            for (int i = 0; i < size; i++)
                if (o.equals(elementData[i]))
                    return i;

        }

//未找到,返回-1

        return -1;

    }

---------------------------------------------------------------------------------------------------------------

反向查找(从数组末尾向开始查找),返回元素的最高索引值

 public int lastIndexOf(Object o) {  
        if (o == null) {  

            for (int i = size-1; i >= 0; i--)   

// 逆序查找  

                if (elementData[i]==null)  
                    return i;  
        } else {  
            for (int i = size-1; i >= 0; i--)  
                if (o.equals(elementData[i]))  
                    return i;  
        }  
        return -1;  

    }  

-------------------------------------------------------------------------------------------------------------------

很明显,就是indexOf()和lastindexOf()都只能执行一次,也就是找到相同内容即返回,所以并没有找出所有的相同元素,而是找到了相对方法中最近的一个,区别o==null和o.eauals()方法,表示null的哈希和内容都相等。

-------------------------------------------------------------------------------------------------------------------


 public Object clone() {  

        try {  

 // Object 的克隆方法:会复制本对象及其内所有基本类型成员和 String 类型成员,但不会复制对象成员、引用对象  (原因是Arrays.copyOf()的方法

            ArrayList<?> v = (ArrayList<?>) super.clone();  

// protected native Object clone() throws CloneNotSupportedException;

  // 对需要进行复制的引用变量,进行独立的拷贝:将存储的元素移入新的 ArrayList 中  

            v.elementData = Arrays.copyOf(elementData, size);  

//v是ArrayList的对象,elementData是对象内存放的object数组名,所以要调用

            v.modCount = 0;  
            return v;  
        } catch (CloneNotSupportedException e) {  
            throw new InternalError(e);  
        }  

    }  

---------------------------------------------------------------------------------------------------------------

返回 ArrayList 的 Object 数组 
     包含 ArrayList 的所有储存元素 
     对返回的该数组进行操作,不会影响该 ArrayList(相当于分配了一个新的数组)==>该操作是安全的 
     元素存储顺序与 ArrayList 中的一致 

 public Object[] toArray() {
        return Arrays.copyOf(elementData, size);

    }

-------------------------------------------------------------------------------------------------------------------

返回 ArrayList 元素组成的数组 
     * @param a 需要存储 list 中元素的数组 
     * 若 a.length >= list.size,则将 list 中的元素按顺序存入 a 中,然后 a[list.size] = null, a[list.size + 1] 及其后的元素依旧是 a 的元素 
     * 否则,将返回包含list 所有元素且数组长度等于 list 中元素个数的数组 
     * 注意:若 a 中本来存储有元素,则 a 会被 list 的元素覆盖,且 a[list.size] = null 
     * @return  
     * @throws ArrayStoreException 当 a.getClass() != list 中存储元素的类型时 
     * @throws NullPointerException 当 a 为 null 时 
     */  

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;
    }

----------------------------------------------------------------------------------------------------------------

ArrayList提供了一个将List转为数组的一个非常方便的方法toArray。toArray有两个重载的方法:

  (1)list.toArray();

  (2)list.toArray(T[]  a);

  对于第一个重载方法,是将list直接转为Object[] 数组;

  第二种方法是将list转化为你所需要类型的数组,当然我们用的时候会转化为与list内容相同的类型。

----------------------------------------------------------------------------------------------------------------





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值