zoukankan      html  css  js  c++  java
  • ArrayList源码解析

    一、ArrayList认识

      ArrayList是可以动态增长和缩减的索引序列,它是基于数组实现的List类。如图

     二、源码解析

      内部存储元素是数组,默认数据大小是10,下面介绍常用方法。

    2.1、构造方法

    • ArrayList():构造一个初始容量为10的空列表
    • ArrayList( int initialCapcity ):构造一个具有初始容量值得空列表
    • ArrayList(Collection<?extend E> c):构造一个包含指定元素的列表
    //第一种
        public ArrayList() {
            this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
        }
        //    第二种
        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);
            }
        }
        //第三种
        public ArrayList(Collection<? extends E> c) {
            elementData = c.toArray();
            if ((size = elementData.length) != 0) {
                if (elementData.getClass() != Object[].class)
                    elementData = Arrays.copyOf(elementData, size, Object[].class);
            } else {
                this.elementData = EMPTY_ELEMENTDATA;
            }
        }
    

     2.2、增删改查操作

    对于增删改查的基本操作,在这里只给出一些比较重要的源代码,实现起来比较简单的就不给出了。

    (1)增加元素

    • add(E e):添加一个元素到列表的末尾。
    • add( int index, E element ) :在指定的位置添加元素
    • addAll( Collection<? extends E> c ):添加一个集合到元素的末尾.以上返回类型是boolean
     1     //第一种 尾插
     2     public boolean add(E e) {
     3         //确保数组边界可以存放新元素 size为当前已存放元素个数
     4         ensureCapacityInternal(size + 1);
     5         elementData[size++] = e;
     6         return true;
     7     }
     8     //第二种 插入
     9     public void add(int index, E element) {
    10         rangeCheckForAdd(index);
    11         ensureCapacityInternal(size + 1); 
    12         System.arraycopy(elementData, index, elementData, index + 1,
    13                 size - index);
    14         elementData[index] = element;
    15         size++;
    16     }
    17     //第三种 批量增加
    18     public boolean addAll(Collection<? extends E> c) {
    19         Object[] a = c.toArray();
    20         int numNew = a.length;
    21         ensureCapacityInternal(size + numNew);  // Increments modCount
    22         System.arraycopy(a, 0, elementData, size, numNew);
    23         size += numNew;
    24         return numNew != 0;
    25     }

    (2)删除操作

    • remove(Object o):删除列表中第一个出现O的元素
    • remove( int index):删除列表中指定位置的元素
    • removeAll(Collection<?> c):删除列表中包含C的所有元素
    • removeIf(Predictcate<? super E> filter):删除列表中给定谓词的所有元素
    • removeRange( int from,int to ):删除从from到to的所有元素
    • clear():清除所有的元素。返回类型为void
     1  //第一种
     2     public E remove(int index) {
     3         //1、检查越界
     4         rangeCheck(index);
     5         modCount++;
     6         //2、获取被删除元素
     7         E oldValue = elementData(index);
     8         int numMoved = size - index - 1;
     9         if (numMoved > 0)
    10             //3、index之后的元素迁移一位
    11             System.arraycopy(elementData, index + 1, elementData, index,
    12                     numMoved);
    13         elementData[--size] = null; // clear to let GC do its work
    14         return oldValue;
    15     }
    16     //第二种
    17     public boolean remove(Object o) {
    18         if (o == null) {
    19             for (int index = 0; index < size; index++)
    20                 if (elementData[index] == null) {
    21                     fastRemove(index);
    22                     return true;
    23                 }
    24         } else {
    25             for (int index = 0; index < size; index++)
    26                 if (o.equals(elementData[index])) {
    27                     fastRemove(index);
    28                     return true;
    29                 }
    30         }
    31         return false;
    32     }

    (3)更改操作

    • retainAll( Collection<?> c ):仅仅保留列表中和C相同的元素,相当于&运算
    • set(int index,E element):用element替换index位置上的元素。
    • size():返回此列表的元素数
    • sort(Comparator<? super E> c):按照指定的排序规则排序
    • subList( int from , int to ):返回从from到to之间的列表
    • toArray():将列表转化为数组
    • trimToSize( ):修改当前实例的容量是列表的当前大小。
     1     public E set(int index, E element) {
     2         //1、检查越界
     3         rangeCheck(index);
     4         //2、获取被替换元素
     5         E oldValue = elementData(index);
     6         //3、替换
     7         elementData[index] = element;
     8         //4、返回被替换元素
     9         return oldValue;
    10     }

    (4)查操作

    • contains(Object o):如果包含元素o,则返回为true
    • get(int index):返回指定索引的元素
    • indexOf( Object o ):返回此列表中指定元素的第一次出现的索引,如果列表不包含此元素,返回-1
    • lastindexOf( Object o ):返回此列表中指定元素的最后一次出现的索引,如果列表不包含此元素,返回-1
    • isEmpty():如果列表为空,返回true.
    • iterator():返回列表中元素的迭代器
    • listIterator():返回列表的列表迭代器(按适当的顺序)
    • listIterator(int index):从适当的位置返回列表的列表迭代器(按照正确的顺序)
     1     public boolean contains(Object o) {
     2         return indexOf(o) >= 0;
     3     }
     4     public int indexOf(Object o) {
     5         if (o == null) {
     6             for (int i = 0; i < size; i++)
     7                 if (elementData[i] == null)
     8                     return i;
     9         } else {
    10             for (int i = 0; i < size; i++)
    11                 if (o.equals(elementData[i]))
    12                     return i;
    13         }
    14         return -1;
    15     }

    三、总结

    1、ArrayList 每次容器增长时,都是以1.5倍增长 

    2、每次容器变化时需要将原数组copy到新数组中,使用的方法是系统native 方法  System.arraycopy(src,srcPos,dest,destPos,length)

    3、尽量声明时,预估好容器大小,以免多次容器大小的变化

    4、线程不安全,数组位置使用的是size标记

    一个 ArrayList ,在添加一个元素的时候,它可能会有两步来完成: 
    1. 在 Items[Size] 的位置存放此元素; 
    2. 增大 Size 的值。 
    在单线程运行的情况下,如果 Size = 0,添加一个元素后,此元素在位置 0,而且 Size=1; 
    而如果是在多线程情况下,比如有两个线程,线程 A 先将元素存放在位置 0。
    但是此时 CPU 调度线程A暂停,线程 B 得到运行的机会。线程B也向此 ArrayList 添加元素,因为此时 Size 仍然等于 0
    (注意,添加一个元素是要两个步骤,而线程A仅仅完成了步骤1),
    所以线程B也将元素存放在位置0。然后线程A和线程B都继续运行,都增加 Size 的值。  那好,现在我们来看看 ArrayList 的情况,元素实际上只有一个,存放在位置
    0,而 Size 却等于 2。这就是“线程不安全”了。 

    5、舍弃效率使之变成线程安全的方法:使用synchronized关键字、Collections.synchronizedList();

     
  • 相关阅读:
    android动态主题切换(RRO 技术)
    Android设计模式-单例模式
    Android 设计模式
    简单理解Binder机制的原理
    Android的Activity启动流程分析
    android 多线程实现方式、并发与同步学习总结
    RecyclerView 缓存机制 | 如何复用表项?
    recyclerview 缓存讲解
    csharp中实现图片拖拽
    特征提取的综合实验(多种角度比较SIFT、SURF、BRISK、ORB、FREAK等局部特诊点检测和描述算法)(2021版)
  • 原文地址:https://www.cnblogs.com/HansonYao/p/arrayList.html
Copyright © 2011-2022 走看看