JAVA集合二 ——list(02、ArrayList)

JAVA集合二 ——list(02、ArrayList)
     *特别声明:

     *本文只是备忘录。
     

    

     JAVA中List的实现主要有ArrayList、vector、stack、LinkedList,以及一个抽象的类AbstractList。

1、AbstractList

      AbstractList实现了List接口的一些实现,子类可以覆盖,也可以直接使用。更多的是提供了一些思维和实现方式。

     如:

/**
     * {@inheritDoc}
     *
     * <p>This implementation always throws an
     * {@code UnsupportedOperationException}.
     *
     * @throws UnsupportedOperationException {@inheritDoc}
     * @throws IndexOutOfBoundsException     {@inheritDoc}
     */
    public E remove(int index) {
        throw new UnsupportedOperationException();
    }


    // Search Operations

    /**
     * {@inheritDoc}
     *
     * <p>This implementation first gets a list iterator (with
     * {@code listIterator()}).  Then, it iterates over the list until the
     * specified element is found or the end of the list is reached.
     *
     * @throws ClassCastException   {@inheritDoc}
     * @throws NullPointerException {@inheritDoc}
     */
    public int indexOf(Object o) {
        ListIterator<E> it = listIterator();
        if (o==null) {
            while (it.hasNext())
                if (it.next()==null)
                    return it.previousIndex();
        } else {
            while (it.hasNext())
                if (o.equals(it.next()))
                    return it.previousIndex();
        }
        return -1;
    }

    /**
     * {@inheritDoc}
     *
     * <p>This implementation first gets a list iterator that points to the end
     * of the list (with {@code listIterator(size())}).  Then, it iterates
     * backwards over the list until the specified element is found, or the
     * beginning of the list is reached.
     *
     * @throws ClassCastException   {@inheritDoc}
     * @throws NullPointerException {@inheritDoc}
     */
    public int lastIndexOf(Object o) {
        ListIterator<E> it = listIterator(size());
        if (o==null) {
            while (it.hasPrevious())
                if (it.previous()==null)
                    return it.nextIndex();
        } else {
            while (it.hasPrevious())
                if (o.equals(it.previous()))
                    return it.nextIndex();
        }
        return -1;
    }

      remove方法的含义是让子类来实现该方法,同时通过抛出 UnsupportedOperationException() 来告诉使用者该方法未实现。 

     indexOf(),lastIndexOf(),提供了具体的实现,当然子类也可以覆盖该方法。

     #在来看看集合中一支出现的Iterator和List中的ListIterator,Abstractor也给出了实现方式-----通过私有的内部类。

 private class Itr implements Iterator<E> {
        /**
         * Index of element to be returned by subsequent call to next.
         */
        int cursor = 0;

        /**
         * Index of element returned by most recent call to next or
         * previous.  Reset to -1 if this element is deleted by a call
         * to remove.
         */
        int lastRet = -1;

        /**
         * The modCount value that the iterator believes that the backing
         * List should have.  If this expectation is violated, the iterator
         * has detected concurrent modification.
         */
        int expectedModCount = modCount;

        public boolean hasNext() {
            return cursor != size();
        }

        public E next() {
            checkForComodification();
            try {
                int i = cursor;
                E next = get(i);
                lastRet = i;
                cursor = i + 1;
                return next;
            } catch (IndexOutOfBoundsException e) {
                checkForComodification();
                throw new NoSuchElementException();
            }
        }

        public void remove() {
            if (lastRet < 0)
                throw new IllegalStateException();
            checkForComodification();

            try {
                AbstractList.this.remove(lastRet);
                if (lastRet < cursor)
                    cursor--;
                lastRet = -1;
                expectedModCount = modCount;
            } catch (IndexOutOfBoundsException e) {
                throw new ConcurrentModificationException();
            }
        }

        final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }
    }

    private class ListItr extends Itr implements ListIterator<E> {
        ListItr(int index) {
            cursor = index;
        }

        public boolean hasPrevious() {
            return cursor != 0;
        }

        public E previous() {
            checkForComodification();
            try {
                int i = cursor - 1;
                E previous = get(i);
                lastRet = cursor = i;
                return previous;
            } catch (IndexOutOfBoundsException e) {
                checkForComodification();
                throw new NoSuchElementException();
            }
        }

        public int nextIndex() {
            return cursor;
        }

        public int previousIndex() {
            return cursor-1;
        }

        public void set(E e) {
            if (lastRet < 0)
                throw new IllegalStateException();
            checkForComodification();

            try {
                AbstractList.this.set(lastRet, e);
                expectedModCount = modCount;
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }

        public void add(E e) {
            checkForComodification();

            try {
                int i = cursor;
                AbstractList.this.add(i, e);
                lastRet = -1;
                cursor = i + 1;
                expectedModCount = modCount;
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }
    }

       通过私有的内部类来实现迭代器,可以避免其他类的使用。

        ArayyList、vector、LinkedList都是通过如此实现自己的迭代器。

2、ArrayList

      ArrayList提供了一种快速的集合方式,通过索引获取元素速度会很快,插入和删除会比较慢。

     为什么索引快,插入、删除慢?下面慢慢分析。

      其实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. Any
     * empty ArrayList with elementData == EMPTY_ELEMENTDATA will be expanded to
     * DEFAULT_CAPACITY when the first element is added.
     */
    transient Object[] elementData; // non-private to simplify nested class access

    /**
     * The size of the ArrayList (the number of elements it contains).
     *
     * @serial
     */
    private int size;

    ArrayList中提供了一个数组elementData用来保存所有的对象。其实是存在数组中的,所有对ArrayList的操作可以说都是对elementData数组的操作。

   这样ArrayList通过索引获取元素实际是通过 数组直接获取元素,这当然快。

    get()方法如下:

    public E get(int index) {
        rangeCheck(index);

        return (E) elementData[index];
    }
 

     

      #我们知道数组的大小在创建的时候是需要指定的,但是我们的ArrayList可以不用指定大小,而可以自己根据元素的数量来自动定义大小。这是如何实现的呢

       ArrayList通过动态扩容来实现上面的问题。如何扩容?

       首先看看我们new 一个ArrayList的时候发生了什么。看看2个构造方法。

  /**
     * Constructs an empty list with the specified initial capacity.
     *
     * @param  initialCapacity  the initial capacity of the list
     * @throws IllegalArgumentException if the specified initial capacity
     *         is negative
     */
    public ArrayList(int initialCapacity) {
        super();
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        this.elementData = new Object[initialCapacity];
    }

    /**
     * Constructs an empty list with an initial capacity of ten.
     */
    public ArrayList() {
        super();
        this.elementData = EMPTY_ELEMENTDATA;
    }


        上面的2中构造方法,一个提供了初始化的大小,一个没有。如果提供了大小,则初始化存储元素的数组大小为给定的大小;否则,初始化为空数组。

         所以当添加元素的时候,当已经超过数组的大小的时候。这个时候就会动态扩容。

        如何扩容:

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

         add方法在数组的最后一位添加元素。

         添加的第一步就是扩容,ensureCapacityInternal(size + 1);当然是判断是否需要扩容,如果数组长度大于当前元素的数量,是不需要扩容的。

    private void ensureCapacityInternal(int minCapacity) {
        if (elementData == EMPTY_ELEMENTDATA) {
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }

        ensureExplicitCapacity(minCapacity);
    }

    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }
        

     minCapacity代表插入元素后,数组最小的容量。

    在来看看扩容的方法 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) {    int newCapacity = oldCapacity + (oldCapacity >> 1);


        // overflow-conscious code
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        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:
        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;
    }


    可以看到每次扩容 默认是扩容50%

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

   而且当容量很小的时候是直接扩容到10(这个10是上面ensureCapacityInternal()方法计算得出的。),很大的时候计算大数组的容量。

默认的最大值是MAX_ARRAY_SIZE ,超过则设置为int最大值。

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

所以当创建ArryayList的时候 最好指定一个大小(可以减少扩容操作,不知道大小的时候,给个默认值值10吧)。

要注意,每次扩容后之后会将对象复制到新的数组。

elementData = Arrays.copyOf(elementData, newCapacity);
    

   #另外一个add方法:在指定位置添加元素。

  /**
     * Inserts the specified element at the specified position in this
     * list. Shifts the element currently at that position (if any) and
     * any subsequent elements to the right (adds one to their indices).
     *
     * @param index index at which the specified element is to be inserted
     * @param element element to be inserted
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    public void add(int index, E element) {
        rangeCheckForAdd(index);

        ensureCapacityInternal(size + 1);  // Increments modCount!!
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
        elementData[index] = element;
        size++;
    }

   这个方法首先进行的是扩容。然后将源数组中index之后的元素,放到新数组index+1之后,这样index的位置就空出来了。然后elementData[index] = element; 在index位置插入数据。
   

    总结:新增的时候包括以下步骤,扩容、数组的copy,赋值。所以说插入数据和删除比较慢。(删除同样涉及到数组的copy)


    #删除方法:

  public E remove(int index) {
        rangeCheck(index);

        modCount++;
        E oldValue = elementData(index);

        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; // clear to let GC do its work

        return oldValue;
    }

    /**
     * Removes the first occurrence of the specified element from this list,
     * if it is present.  If the list does not contain the element, it is
     * unchanged.  More formally, removes the element with the lowest index
     * <tt>i</tt> such that
     * <tt>(o==null ? get(i)==null : o.equals(get(i)))</tt>
     * (if such an element exists).  Returns <tt>true</tt> if this list
     * contained the specified element (or equivalently, if this list
     * changed as a result of the call).
     *
     * @param o element to be removed from this list, if present
     * @return <tt>true</tt> if this list contained the specified element
     */
    public boolean remove(Object o) {
        if (o == null) {
            for (int index = 0; index < size; index++)
                if (elementData[index] == null) {
                    fastRemove(index);
                    return true;
                }
        } else {
            for (int index = 0; index < size; index++)
                if (o.equals(elementData[index])) {
                    fastRemove(index);
                    return true;
                }
        }
        return false;
    }

     注意:list循环的时候,删除元素之后,注意索引的值,避免出现数组越界。

                当然for each也会出现问题(一开始以为不会出现问题,应为forEach是使用的Iterator来处理)。但是iterator.next在获取的时候需要检验iterator的改变次数和list的改变次数进行对比。如下代码

   

 public Iterator<E> iterator() {
        return new Itr();
    }

    /**
     * An optimized version of AbstractList.Itr
     */
    private class Itr implements Iterator<E> {
        int cursor;       // index of next element to return
        int lastRet = -1; // index of last element returned; -1 if no such
        int expectedModCount = modCount;//创建iterator的时候已经赋值。

        public boolean hasNext() {
            return cursor != size;
        }

        @SuppressWarnings("unchecked")
        public E next() {
            checkForComodification();
            int i = cursor;
            if (i >= size)
                throw new NoSuchElementException();
            Object[] elementData = ArrayList.this.elementData;
            if (i >= elementData.length)
                throw new ConcurrentModificationException();
            cursor = i + 1;
            return (E) elementData[lastRet = i];
        }
    }

结论:使用Iterator的时候,是不能在对ArrayLIst进行add,remove操作。不应该将ArrayList和Iterator混合使用。

而且foreach可以删除倒数第二个元素而不出错。应为没for循环的时候,会通过hasNext来判断是否有下个元素。代码如上。

  public boolean hasNext() {
            return cursor != size;
        }

倒数第二个元素删除之后,size正好等于cursor。

   int length = list.size();
        //错误的写法,数组越界
        for(int i=0;i<length;i++){
            list.remove(i);
        }
        //错误,
        for(Object o : list){
            list.remove(o);
        }


 



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值