• java集合之ArrayList



    title: java集合之ArrayList
    date: 2018-10-18 10:39:19
    tags: java集合
    author :辰砂


    1. ArrayList概述:

    ArrayList是List接口的可变数组的实现。实现了所有可选列表操作,并允许包括 null 在内的所有元素。除了实现 List 接口外,此类还提供一些方法来操作内部用来存储列表的数组的大小。

    每个ArrayList实例都有一个容量,该容量是指用来存储列表元素的数组的大小。它总是至少等于列表的大小。随着向ArrayList中不断添加元素,其容量也自动增长。自动增长会带来数据向新数组的重新拷贝,因此,如果可预知数据量的多少,可在构造ArrayList时指定其容量。在添加大量元素前,应用程序也可以使用ensureCapacity操作来增加ArrayList实例的容量,这可以减少递增式再分配的数量。

    继承自 AbstractList,实现了 List 接口。底层基于数组实现容量大小动态变化。允许 null 的存在。同时还实现了 RandomAccess、Cloneable、Serializable 接口,所以ArrayList 是支持快速访问、复制、序列化的。

    2.ArrayList的用法:

    参考:https://www.cnblogs.com/kungfupanda/p/7357142.html

    public class ArrayListTest {
    
        public static void main(String[] args) {
    
            // 创建一个空的数组链表对象list,list用来存放String类型的数据
            ArrayList<String> list = new ArrayList<String>();
    
            // 增加元素到list对象中
            list.add("Item1");
            list.add("Item2");
            list.add(2, "Item3"); // 此条语句将会把“Item3”字符串增加到list的第3个位置。
            list.add("Item4");
    
            // 显示数组链表中的内容
            System.out.println("The arraylist contains the following elements: "
                    + list);
    
            // 检查元素的位置
            int pos = list.indexOf("Item2");
            System.out.println("The index of Item2 is: " + pos);
    
            // 检查数组链表是否为空
            boolean check = list.isEmpty();
            System.out.println("Checking if the arraylist is empty: " + check);
    
            // 获取链表的大小
            int size = list.size();
            System.out.println("The size of the list is: " + size);
    
            // 检查数组链表中是否包含某元素
            boolean element = list.contains("Item5");
            System.out
                    .println("Checking if the arraylist contains the object Item5: "
                            + element);
    
            // 获取指定位置上的元素
            String item = list.get(0);
            System.out.println("The item is the index 0 is: " + item);
    
            // 遍历arraylist中的元素
    
            // 第1种方法: 循环使用元素的索引和链表的大小
            System.out
                    .println("Retrieving items with loop using index and size list");
            for (int i = 0; i < list.size(); i++) {
                System.out.println("Index: " + i + " - Item: " + list.get(i));
            }
    
            // 第2种方法:使用foreach循环
            System.out.println("Retrieving items using foreach loop");
            for (String str : list) {
                System.out.println("Item is: " + str);
            }
    
            // 第三种方法:使用迭代器
            // hasNext(): 返回true表示链表链表中还有元素
            // next(): 返回下一个元素
            System.out.println("Retrieving items using iterator");
            for (Iterator<String> it = list.iterator(); it.hasNext(); ) {
                System.out.println("Item is: " + it.next());
            }
    
            // 替换元素
            list.set(1, "NewItem");
            System.out.println("The arraylist after the replacement is: " + list);
    
            // 移除元素
            // 移除第0个位置上的元素
            list.remove(0);
    
            // 移除第一次找到的 "Item3"元素
            list.remove("Item3");
    
            System.out.println("The final contents of the arraylist are: " + list);
    
            // 转换 ArrayList 为 Array
            String[] simpleArray = list.toArray(new String[list.size()]);
            System.out.println("The array created after the conversion of our arraylist is: "
                    + Arrays.toString(simpleArray));
        }
    
    

    3. ArrayList的实现:

    1.成员变量

        /**
         * Default initial capacity.
         * 数组默认的大小
         */
        private static final int DEFAULT_CAPACITY = 10;
    
        /**
         * Shared empty array instance used for empty instances.
         */
        private static final Object[] EMPTY_ELEMENTDATA = {};
    
        /**
         * Shared empty array instance used for default sized empty instances. We
         * distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
         * first element is added.
         * 
         * 第一次添加元素时知道该 elementData 从空的构造函数还是有参构造函数被初始化的。
         * 以便确认如何扩容
         */
        private static final Object[] DEFAULTCAPACITY_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 == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
         * will be expanded to DEFAULT_CAPACITY when the first element is added.
         * 
         * 基于数组实现,保存元素的数组使用 transient 修饰,transient该关键字声明数组默认不会被序列化。
         * ArrayList 具有动态扩容特性,因此保存元素的数组不一定都会被使用,那么就没必要全部进行序列化。
         * ArrayList 重写了 writeObject() 和 readObject() 来控制只序列化数组中有元素填充那部分内容。
         */
        transient Object[] elementData; // non-private to simplify nested class access
    
        /**
         * The size of the ArrayList (the number of elements it contains).
         * 
         * size 是指 elementData 中实际有多少个元素,而 elementData.length 为集合容量,
         * 表示最多可以容纳多少个元素
         * @serial
         */
        private int size;
    
    

    2.构造方法:

    1.构造一个默认初始容量为10的空列表

    /**
         * Increases the capacity of this <tt>ArrayList</tt> instance, if
         * necessary, to ensure that it can hold at least the number of elements
         * specified by the minimum capacity argument.
         *
         * @param   minCapacity   the desired minimum capacity
         */
        public void ensureCapacity(int minCapacity) {
            int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
                // any size if not default element table
                ? 0
                // larger than default for default empty table. It's already
                // supposed to be at default size.
                : DEFAULT_CAPACITY;
    
            if (minCapacity > minExpand) {
                ensureExplicitCapacity(minCapacity);
            }
        }
        
    

    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
         *         
         * initialCapacity 参数,如果为空的话 ,初始化EMPTY_ELEMENTDATA,如果不为空,new Object[].     
         */
        public ArrayList(int initialCapacity) {
            if (initialCapacity > 0) {
                this.elementData = new Object[initialCapacity];
            } else if (initialCapacity == 0) {
                this.elementData = EMPTY_ELEMENTDATA;
            } else {
                throw new IllegalArgumentException("Illegal Capacity: "+
                                                   initialCapacity);
            }
        }
       
    

    3.构造一个包含指定collection的元素的列表

      /**
         * Constructs a list containing the elements of the specified
         * collection, in the order they are returned by the collection's
         * iterator.
         *
         * @param c the collection whose elements are to be placed into this list
         * @throws NullPointerException if the specified collection is null
         */
        public ArrayList(Collection<? extends E> c) {
            elementData = c.toArray();
            if ((size = elementData.length) != 0) {
                // c.toArray might (incorrectly) not return Object[] (see 6260652)
                if (elementData.getClass() != Object[].class)
                    elementData = Arrays.copyOf(elementData, size, Object[].class);
            } else {
                // replace with empty array.
                this.elementData = EMPTY_ELEMENTDATA;
            }
        }
    

    3.常用方法:

    1.add()

        /**
         * 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) {
        // 判断array有没有足够的空间
            ensureCapacityInternal(size + 1);  // Increments modCount!!
            elementData[size++] = e;
            return true;
        }
        
        private void ensureCapacityInternal(int minCapacity) {
        // 确保array有足够的空间
            ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
        }
        // 计算
         private static int calculateCapacity(Object[] elementData, int minCapacity) {
            if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
                return Math.max(DEFAULT_CAPACITY, minCapacity);
            }
            return minCapacity;
        }
        
        
        private void ensureExplicitCapacity(int minCapacity) {
            modCount++;
    
            // overflow-conscious code 如果没有足够的空间,就需要进行扩容
            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
         * 
         * 默认将扩容至原来容量的 1.5 倍。但是扩容之后也不一定适用,有可能太小,有可能太大。所以才会有下面两
         * 个 if 判断。如果1.5倍太小的话,则将我们所需的容量大小赋值给newCapacity,如果1.5倍太大或者我们需
         * 要的容量太大,那就直接拿 newCapacity = (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE 
         * 来扩容。然后将原数组中的数据复制到大小为 newCapacity 的新数组中,并将新数组赋值给 elementData
         */
        private void grow(int minCapacity) {
            // 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);
        }
        
    

    2.remove()

        /**
         * Removes the element at the specified position in this list.
         * Shifts any subsequent elements to the left (subtracts one from their
         * indices).
         *
         * @param index the index of the element to be removed
         * @return the element that was removed from the list
         * @throws IndexOutOfBoundsException {@inheritDoc}
         * 
         * 当我们调用 remove(int index) 时,首先会检查 index 是否合法,然后再判断要删除的元素是否位于数组的最后一个位置。
         * 如果 index 不是最后一个,就再次调用 System.arraycopy() 方法拷贝数组。说白了就是将从 index + 1 开始向后所有的元素都向前挪一个位置。
         * 然后将数组的最后一个位置空,size - 1。如果 index 是最后一个元素那么就直接将数组的最后一个位置空,size - 1即可。
         *  当我们调用 remove(Object o) 时,会把 o 分为是否为空来分别处理。然后对数组做遍历,找到第一个与 o 对应的下标 index,
         *  然后调用 fastRemove 方法,删除下标为 index 的元素。其实仔细观察 fastRemove(int index) 方法和 remove(int index) 方法基本全部相同。
         */
        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;
        }
    

    3.get()

    public E get(int index) {
            rangeCheck(index);
    
            return elementData(index);
        }
    

    4.Fail-Fast机制

    ArrayList也采用了快速失败的机制,通过记录modCount参数来实现。在面对并发的修改时,迭代器很快就会完全失败,而不是冒着在将来某个不确定时间发生任意不确定行为的风险。fail-fast 机制,即快速失败机制,是java集合(Collection)中的一种错误检测机制。当在迭代集合的过程中该集合在结构上发生改变的时候,就有可能会发生fail-fast,即抛出ConcurrentModificationException异常。fail-fast机制并不保证在不同步的修改下一定会抛出异常,它只是尽最大努力去抛出,所以这种机制一般仅用于检测bug

    1.举例 使用迭代器的过程中,执行了remove的操作

      public static void main(String[] args) {
               List<String> list = new ArrayList<>();
               for (int i = 0 ; i < 10 ; i++ ) {
                    list.add(i + "");
               }
               Iterator<String> iterator = list.iterator();
               int i = 0 ;
               while(iterator.hasNext()) {
                    if (i == 3) {
                         list.remove(3);
                    }
                    System.out.println(iterator.next());
                    i ++;
               }
         }
    
    
  • 相关阅读:
    Openstack 通过 SQLAlchemy-ORM 访问数据库
    ulimit -c unlimited
    ajax 调用后台接口示例
    读书有什么用——北漂18年(番外篇三)
    zTree点击文字勾选复选框
    深度剖析 | 基于大数据架构的BI应用
    深度剖析 | 基于大数据架构的BI应用
    AngularJS之对话框
    AngularJS之依赖注入(实例一)
    AngularJS之$watch方法(监控动作)
  • 原文地址:https://www.cnblogs.com/tojian/p/9814759.html
Copyright © 2020-2023  润新知