集合类源码简单阅读(一)(ArrayList)

来源:互联网 发布:网贷安卓源码 编辑:程序博客网 时间:2024/06/12 03:37

      在休憩不知道该啃哪根骨头的情况下,选择了一根比较比较好啃的骨头,就是从Java基础知识开始,当然是源码,而集合就自然成为了要研究和记录了第一个源码(记录单纯是为了可以给自己看到一个学习轨迹,不足的地方有待大家提意见)

     由于接下来会陆陆续续研究集合的源码,因此还是很传统的把这张图给贴出来了

    

 

  六个接口:

        Iterator,接口
        Collection<E>,接口, Collection<E> extends Iterable<E>
        Map ,接口 Map<K,V>
        ListIterator<E>,接口, ListIterator<E> extends Iterator<E>
        List, 接口, List<E> extends Collection<E>
        Set,接口,Set<E> extends Collection<E>
        SortedMap<K, V> ,接口,SortedMap<K,V> extends Map<K,V>

  Collection的继承结构:

       Collection<--List<--Vector
       Collection<--List<--ArrayList
       Collection<--List<--LinkedList
       Collection<--Set<--HashSet
       Collection<--Set<--HashSet<--LinkedHashSet
       Collection<--Set<--SortedSet<--TreeSet

    一、ArrayList(线性列表)
     说明:底层的数据结构就是数组,数组元素类型为Object类型,即可以存放所有类型数据。我们对ArrayList类的实例的所有的操作底层都是基于数组的
     1、Vector、ArrayList 都是以类似数组的形式存储在内存中,LinkedList 则以链表的形式进行存储。
     2、Vector 线程同步,ArrayList、LinkedList 线程不同步;后两者一般用在线程安全的地方,也可以通过 Collections.synchronizedList(……);实现线程同步。
     3、LinkedList 适合指定位置插入、删除操作,不适合查找;ArrayList、Vector适合查找,不适合指定位置的插入、删除操作。
     4、ArrayList 在元素填满容器  时会自动扩充容器大小的 50%,而 Vector 则是100%,因此 ArrayList 更节省空间。
     5、LinkedList 还实现了 Queue 接口,该接口比 List 提供了更多的方法,包括offer(),peek(),poll()等,多与一些线程池一起使用.

    补充:以上都实现了序列化接口,这点在对象远程传输时很重要;而Vector,ArrayList 实现了 AccessRandom 接口,这是一个标记接口,此接口的主要目的是允许 一般的算法更改其行为,从而在将其应用到随机或连续访问列表时能提供良好的性能

         2.1 类的继承

   class ArrayList<E> extends AbstractList<E>  implements List<E>, RandomAccess(可随机访问),     Cloneable(可拷贝), java.io.Serializable(序列化)

      2.2 类的属性  
         
       //序列化编号       private static final long serialVersionUID = 8683452581122892189L;        //初始容量        private static final int DEFAULT_CAPACITY = 10;        //用于空实例的共享空数组实例。        private static final Object[] EMPTY_ELEMENTDATA = {};       //共享空数组实例,用于默认大小的空实例。        private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};       //存储ArrayList元素的数组缓冲区。        transient Object[] elementData;         protected transient int modCount = 0;        //数组实际大小        private int size;       //要分配的数组的最大大小。        private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

     2.3  核心方法

           1) add()方法

        public boolean add(E e) {   //增加元素            ensureCapacityInternal(size + 1);  // 调用ensureCapacityInternal方法            elementData[size++] = e;            return true;        }
         
          这只是第一步,接下来是ensureCapacityInternal(int minCapacity)方法
      
 private void ensureCapacityInternal(int minCapacity) {            if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {  //在初期时默认赋值minCapacity = 10                minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);            }            ensureExplicitCapacity(minCapacity); //调用ensureExplicitCapacity方法        }

      然后是ensureExplicitCapacity(int minCapacity)方法

   

  private void ensureExplicitCapacity(int minCapacity) {        modCount++;          // overflow-conscious code        if (minCapacity - elementData.length > 0)   //是否为容器增容,初始为数组创建默认长度为10的空间            grow(minCapacity);    }      

     最后是grow(int minCapacity)        
        private void grow(int minCapacity) {        int oldCapacity = elementData.length;                 //旧容量       int newCapacity = oldCapacity + (oldCapacity >> 1);   //新容量为旧容量的1.5倍(右位移)       if (newCapacity - minCapacity < 0)                    //新容量小于指定参数容量            newCapacity = minCapacity;                if (newCapacity - MAX_ARRAY_SIZE > 0)                //新容量大于最大容量            newCapacity = hugeCapacity(minCapacity);               elementData = Arrays.copyOf(elementData, newCapacity);  //拷贝扩容
       新容量超过最大最大容量的时候
  
 private static int hugeCapacity(int minCapacity) {        if (minCapacity < 0) // overflow            throw new OutOfMemoryError();        return (minCapacity > MAX_ARRAY_SIZE) ?              Integer.MAX_VALUE :            MAX_ARRAY_SIZE;    }
       2) set(int index,E elemnet)方法
          由于ArrayList的本质是Object[],因此在设定ArrayList指定位置的值只需要将旧值覆盖即可
  

     public E set(int index, E element) {   //index 设置值的下标  element 要设定的值            rangeCheck(index);               //checkindex            E oldValue = elementData(index);  //获取久值            elementData[index] = element;    // 将element赋值给数组指定的下标            return oldValue;                 //返回旧值        }
      从上面看来set方法结构很简单,checkindex,然后覆盖值   
   
   private void rangeCheck(int index) {        if (index >= size)      //比较index与size的大小            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));    }
    获取指定位置的值方法
   
 @SuppressWarnings("unchecked")  //字符串{ @ code“unchecked”}被用来抑制未经检查的警告。    E elementData(int index) {        return (E) elementData[index];    }
     3) add(int index, E element) 方法
   在指定位置加值,调用ensureCapacityInternal(int minCapacity)确定数组大小,如果长度不够就扩容,然后新建一个新数组将旧数组
    index之前的值以及index后的值复制到新数组,最后把参数element赋值给新数组的指定下标

   

public void add(int index, E element) {        rangeCheckForAdd(index);    //checkindex,与set里面一致        ensureCapacityInternal(size + 1);  // Increments modCount!!        System.arraycopy(elementData, index, elementData, index + 1,                         size - index);        elementData[index] = element;        size++;    }