zoukankan      html  css  js  c++  java
  • java基础之集合长度可变的实现原理

    首先我们要明白java中的集合Collection,List,ArrayList之间的关系:

      ArrayList是具体的实现类,实现了List接口

      List是接口,继承了Collection接口

      List继承了Collection接口   但是List是可以重复的并且有序的集合 Collection是不可重复且无序的

    这里我们先讲一下List集合:

    List接口不能被构造 也就是我们说的不能创建实例对象 但是我们可以像下面那样为List接口创建一个指向自己的对象引用 而ArrayList实现类的实例对象就在这充当了这个指向List接口的对象引用 这也是多态的一种:

    List<String> list = new ArrayList<String>();

    那么现在问题来了 

           为什么要用 List list = new ArrayList()  而不用 ArrayList alist = new ArrayList()呢?

           问题就在于List接口有多个实现类  现在你用的是ArrayList  也许哪一天你需要换成其它的实现类 如 LinkedList或者Vector等等 这时你只要改变这一行就行了:

      List list = new LinkedList();   其它使用了list地方的代码根本不需要改动

           假设你开始用ArrayList alist = new ArrayList()  那需要改的地方就很多了  特别是如果你使用了ArrayList实现类特有的方法和属性。

      这样的好处也是为了代码的可维护性 可复用性 可扩展性以及灵活性 再者就是这符合了里氏代换原则和开闭原则

    言归正传 我们下面说一下 集合的长度为什么是不固定的

    我们知道集合的底层其实也是用数组实现的 那么为什么定义集合的时候 是不需要给出size的 而数组在定义的时候就需要给出长度?

    首先我们分析一下ArrayList的无参构造方法:

        /**
         * Constructs an empty list with an initial capacity of ten.
         */
        public ArrayList() {
            super();
            this.elementData = EMPTY_ELEMENTDATA;
        }
    
        /**
         * Default initial capacity.
         */
        private static final int DEFAULT_CAPACITY = 10;
    
        /**
         * Shared empty array instance used for empty instances.
         */
        private static final Object[] EMPTY_ELEMENTDATA = {};
    
        /**
         * The array buffer into which the elements of the ArrayList are stored.
         * The capacity of the ArrayList is the length of this array buffer. Any
         * empty ArrayList with elementData == EMPTY_ELEMENTDATA will be expanded to
         * DEFAULT_CAPACITY when the first element is added.
         */
        private transient Object[] elementData;

    我们发现无参的构造方法里面 this.elementData = EMPTY_ELEMENTDATA;  相当于给集合了一个空的数组 而且上面代码紫色部分说在第一次给集合添加元素的时候 会把 DEFAULT_CAPACITY 也就是10设置成数组长度

    那么我们通过ArrayList中的add方法看一看是否是这样子:

        /**
         * The size of the ArrayList (the number of elements it contains).
         *
         * @serial
         */
        private int size;
    
        /**
         * Appends the specified element to the end of this list.
         *
         * @param e element to be appended to this list
         * @return <tt>true</tt> (as specified by {@link Collection#add})
         */
        public boolean add(E e) {
            ensureCapacityInternal(size + 1);  // Increments modCount!!
            elementData[size++] = e;
            return true;
        }
    
        private void ensureCapacityInternal(int minCapacity) {
         //这里elementData==EMPTY_ELEMENTDATA 也就是上面无参构造方法里的的赋值, 所以这里的判断可以理解为是否是第一次添加元素时调用此方法
    if (elementData == EMPTY_ELEMENTDATA) { //如果是第一次添加元素 minCapacity应该为0+1 所以这里把DEFAULT_CAPACITY也就是10赋值给minCapacity minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); } ensureExplicitCapacity(minCapacity); } private void ensureExplicitCapacity(int minCapacity) { modCount++; // overflow-conscious code //这里判断新添加一个元素以后 长度是否大于当前数组 如果大于则给数组扩容
         //如果是第一次添加元素 肯定是true 然后把10传到grow方法中去
    if (minCapacity - elementData.length > 0) grow(minCapacity); } /** * Increases the capacity to ensure that it can hold at least the * number of elements specified by the minimum capacity argument. * * @param minCapacity the desired minimum capacity */ private void grow(int minCapacity) { // overflow-conscious code int oldCapacity = elementData.length; //这里是给扩容后的数组定义的长度 int newCapacity = oldCapacity + (oldCapacity >> 1);
         //如果是第一次添加元素 new肯定是小于min的 所以把10赋给newCapacity 用来创建长度为10的新数组
    if (newCapacity - minCapacity < 0) newCapacity = minCapacity; if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); // minCapacity is usually close to size, so this is a win: //把原来的数组copy到新数组中 如果是第一次add 则创建了一个长度为10的数组 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; }

    通过上面的代码 我们可以发现:

    在第一次给集合添加元素的时候 的确会通过add方法及方法内调用的其他方法 创建一个长度为10的数组

    并且以后每次add的时候 都会先判断一下 size+1是否超过了数组的长度 如果超过了长度就重新定义一个长度  int newCapacity = oldCapacity + (oldCapacity >> 1);的数组 然后把旧数组复制到新创建的数组中返回

    解释一下 int newCapacity = oldCapacity + (oldCapacity >> 1);    (oldCapacity >> 1)的意思是oldCapacity转换成2进制然后右移一位 也就是oldCapacity /2

    综上所述 第一次给集合添加元素的时候 集合中数组的长度会被设置成10   每次数组元素满了以后 重新给数组设置的长度为  原数组长度+(原数组长度/2) (这里跟C#不同,C#中是初始长度为4 新数组长度=原数组长度*2)

  • 相关阅读:
    bzoj 1017 魔兽地图DotR
    poj 1322 chocolate
    bzoj 1045 糖果传递
    poj 3067 japan
    timus 1109 Conference(二分图匹配)
    URAL 1205 By the Underground or by Foot?(SPFA)
    URAL 1242 Werewolf(DFS)
    timus 1033 Labyrinth(BFS)
    URAL 1208 Legendary Teams Contest(DFS)
    URAL 1930 Ivan's Car(BFS)
  • 原文地址:https://www.cnblogs.com/blazeZzz/p/9104343.html
Copyright © 2011-2022 走看看