1.类说明

  类注释第一段就说明了ArrayList是什么——是List接口的可变大小的数组实现,实现了所有的list操作,并允许任何元素(包括null),该类跟Vector基本一致,只是该类是线程不同步的。

  其中size、isEmpty、get、set、iterator和listIterator的操作时间是恒定的O(1),add操作的运行时间是O(n)。其余的操作运行时间是一个线性时间(这个不是很明白)。 

  ArrayList其中拥有一个capacity属性,表示该集合存储元素的大小,最小至少是集合的大小,集合每增加一个元素,这个属性值都会自动增长。应用中我们可以指定capacity的数量,这样我们可以减少重新给capacity赋值的操作,以提高效率(在新增操作之前,有一个ensureCapacity的操作)。

  由于arraylist不是同步的,所以在多线程访问的过程中,需要考虑数据安全的问题,我们可以通过List list = Collections.synchronizedList(new ArrayList(...))来获取一个同步的arraylist。

  快速失败:由于该集合是非同步的,所以在多线程的情况下可能出现多个线程同时操作一个对象。Java对于提供了一个错误检测机制,即某个线程在对集合进行迭代时,不允许别的线程对该集合进行结构上的修改(structurally modified)。对快速失败感兴趣的可以查看这篇博客:http://www.jb51.net/article/84468.htm。

 1 public static void main(String[] args) {
 2     List<String> lists = new ArrayList<String>();
 3     lists.add("111");
 4     lists.add("2222");
 5     lists.add("3333");
 6     lists.add("44444");
 7     // 增强for循环和iterator的效果是一样的,也就是说增强for内部就是调用iterator实现的(可以查看编译后的代码
 8     // 或者debug ArrayList源码,会发现remove一次之后进入了Itr中的checkForComodification方法,也间接说明这点)
 9     for (String list : lists) {
10         if (list.length() == 4) {
11             lists.remove(list);
12         }
13     }
14     // 该for可以正常运行,但是达不到我们要的效果,因为删除之后index改变了
15     for (int i = 0; i < lists.size(); i++) {
16         if(lists.get(i).length() == 4){
17             lists.remove(i);
18         }
19     }
20     System.out.println(lists.toString());
21 }
View Code

 

2.继承和实现

  可以看出该类实现了List、RandomAccess、Cloneable和Serializable,继承与AbstractList。其中AbstractList也实现了List,主要是一些集合的操作;RandomAccess是一个空接口,注释上说,该接口最原始(最主要的?)的目的是允许通用算法更改其行为,以便在应用于随机访问列表或顺序访问列表时提供良好的性能。一般空接口就是一种表示,就像Cloneable(表明该对象可以克隆)和Serializable(表明该对象可以序列化)一样,RandomAccess表示该列表是随机访问的,而不是像LinkedList那样是顺序访问(sequential access)的,如果是线性访问列表,则使用for(int i;i<100;i++)这种循环方式要比迭代器循环速度快,如果将随机访问列表的循环方式应用到顺序访问列表的循环方式,会存在一个二次行为,使的效率变低。

  参考博客:http://blog.csdn.net/keda8997110/article/details/8635005

3.成员变量

  int DEFAULT_CAPACITY:默认的数组大小,为10

  Object[] EMPTY_ELEMENTDATA:共享的空数组

  Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA:拥有默认数组大小的空数组

  Object[] elementData:数据缓冲区,其中存储了arrayList的值,当添加第一个元素的时候,会将DEFAULTCAPACITY_EMPTY_ELEMENTDATA的大小扩展到 DEFAULT_CAPACITY,可以看到arraylist的底层是由数组实现的

  int size:数组包含的元素数量

  modCount(继承自AbstractList):数组结构化修改的次数(数组的大小修改),主要应用于fast-fail机制里,如果实现类需要这个机制的话

  MAX_ARRAY_SIZE:Integer.MAX_VALUE - 8,最大的数组长度

4.构造方法

  

 5.成员方法

  void trimToSize():减小当前数组的长度,因为arrayList在申请数组空间的时候一般会多申请0.5倍空间,应用可以通过该方法将多申请的空间释放出来,已达到减少内存或者减少网络请求流量等作用;源码如下:

  

  void ensureCapacity(int minCapacity):自定义数组大小。定义数组大小为size,如果该数组为空数组,则size = 10,如果不为空并且minCapacity的大小大于当前arraylist的长度,就会1.5倍扩容,如果扩容后的大小小于minCapacity,就用minCapacity作为数组最终大小,如果扩容大小大于MAX_ARRAY_SIZE,判断minCapacity是不是大于MAX_ARRAY_SIZE,大于取Integer.MAX_VALUE,小于取MAX_ARRAY_SIZE。最终将扩容数组赋值给elementData。源码如下:

  

  int size()、boolean isEmpty()、boolean contains(Object o)、int indexOf()、int lastIndexOf()、void clear()比较简单,忽略;

  Object clone()方法时浅克隆,需要注意这一点,克隆之后将克隆得到的数组modCount赋值为0;

  Object[] toArray()和T[] toArray(T[] a):二者的区别是前者直接将集合转换成对象数组,后者是将集合(list)转换成某个特定数组(T[] a),如果a.length<=list.size(),则返回的特定数组中都是list的元素,如果a.length>list.size(),则返回的特定数组前list.size()的数据,来自于list,list.size()->a.length取自于a,这里不管a.size的值是不是null,在返回的结果中,该值都被改为null,之后的值都从a[size+1]开始取值;

  a.length=lists.size(),运行结果

  a.length>lists.size(),运行结果

  注释里面说如果调用者知道list里面没有null值得话,这样对确定list的length是有用的(我没想到什么样的应用场景需要这个,有人知道的话望告知)。

  E get(int index)、E set(int index, E element):二者都有个rangeCheck方法,index超过数组大小,抛出IndexOutOfBoundsException。get()返回index对应数据,set()返回该index对应老数据;

  boolean add(E element)、void add(int index, E element)、boolean addAll(Collection<? extends E> c)、boolean addAll(int intdex, Collection<? extends E> c):这些都属于元素的新增,第一个是在数组末尾添加一个元素;第二个是在指定位置添加一个元素,如果指定位置<0或者>size,都会抛出IndexOutOfBoundsException,方法使用了System.arraycopy(ori,index1,des,index2,length)方法,该方法是数组复制,将ori数组从index位置开始复制size-index个元素,将该值从index+1的位置复制给des数组,在这里相当于整体后移一位,最后将新增值赋值给elementData[index],集合长度+1;第三个方法现将集合c转换成数组,然后再将这个数组通过System.arraycopy的方法追加到elementData的末尾;第四个方法跟第三个大同小异,不多赘述;

  E remove(int index)、boolean remove(Object o)、void removeRange(int fromIndex, int toIndex):前两者如下图所示:

  

  第三个删除是含头不含尾的删除一个区域,fromIndex位置删除,toIndex位置不删除,源码和上面类似,不赘述;

  boolean retainAll(Collection<?> c)和boolean removeAll(Coolection<?> c):这二者也是删除元素的一种,前者是删除c集合以外的元素,后者是删除c包含的元素,c不能为空,否则会报出NullPointerException,起作用的主要是私有的batchRemove方法,分析如下:

 1 private boolean batchRemove(Collection<?> c, boolean complement) {
 2         // final修饰数组表示数组的引用不能再指向其他对象,但是本身元素是可变的
 3         final Object[] elementData = this.elementData;
 4         // Object[] obs = {};
 5         // 编译出错,因为elementData的指向是不可变的
 6         // elementData = obs;
 7         int r = 0, w = 0;
 8         boolean modified = false;
 9         try {
10             // 循环比较c和原数组的元素
11             for (; r < size; r++)
12                 // 不管complement为true还是false,[elementData[0],elementData[w]]存储的都是要保留的数据
13                 if (c.contains(elementData[r]) == complement)
14                     elementData[w++] = elementData[r];
15         } finally {
16             // Preserve behavioral compatibility with AbstractCollection,
17             // even if c.contains() throws.
18             // 这里似乎只有c.contains出现异常,才会出现r != size这种情况,
19             if (r != size) {
20                 System.arraycopy(elementData, r,
21                                  elementData, w,
22                                  size - r);
23                 w += size - r;
24             }
25             // 最终将elementData从w到size位置置为空即可
26             if (w != size) {
27                 // clear to let GC do its work
28                 for (int i = w; i < size; i++)
29                     elementData[i] = null;
30                 modCount += size - w;
31                 size = w;
32                 modified = true;
33             }
34         }
35         return modified;
36     }
View Code

  void writeObject()、void writeObject():这两个方法就是将对象序列化和反序列化的过程;

  接下来的几个方法都涉及到了内部类,只要了解了这几个内部类,我们就会对这些方法有很清晰的人是,我们来解释一下这几个内部类吧,Itr、ListItr和SubList。

  Itr:实现类Iterator,去看Iterator源码,注释上面说“集合的迭代器,在java的框架中取代了Enumeration”,Enumeration的注释建议大家使用Iterator,因为Iterator提供了remove方法和更短的方法名……迭代器删除方法上面有一句话叫“每当next()方法调用一次,remove()方法才可以调用一次”,第二次调用会抛出异常,如下:

 1 public static void main(String[] args) {
 2     List<Integer> b = new ArrayList<Integer>(2);
 3     b.add(1);
 4     b.add(2);
 5     Iterator<Integer> iterator = b.iterator();
 6     System.out.println("next:" + iterator.next());
 7     iterator.remove();
 8     System.out.println("first remove end");
 9     iterator.remove();
10     System.out.println("second remove end");
11     iterator.remove();
12     System.out.println("third remove end");
13 }
View Code

  运行结果:

  这个内部类的方法相对简单,看完代码我们知道为什么会要求调用一次next()方法,再调用一次remove()方法了。唯独forEachRemaining这个方法,涉及到jdk1.8函数式接口,下面会简单说明。

  ListItr:继承Itr,实现ListIterator,主要区别添加了对上一个元素的获取、设置等操作,也就意味着可以双向索引值。源码相对简单,不做赘述。

  二者区别:http://www.linuxidc.com/Linux/2014-11/109950.htm

  SubList:只有有参构造方法,从原数组集合中拷贝出一份从fromIndex(包含)到toIndex(不包含)的数组集合出来,对该数组集合的操作不会影响到原始数据,如果fromIndex==toIndex,返回空数组集合,数组集合拥有一切arrayList所有的操作。

6.jdk1.8新方法

  List新增了void replaceAll(UnaryOperator<E> operator)、void sort(Comparator<? super E> c)、Spliterator<E> spliterator()三个方法;

  Collection中新增了boolean removeIf(Predicate<? super E> filter)、Stream<E> stream()、Stream<E> parallelStream()三个方法;

  Iterable中新增了void forEach(Consumer<? super T> action)方法。

  对于Lambda表达式我并不了解,上述方法的作用可以参考这篇博客:http://www.cnblogs.com/CarpenterLee/p/6507161.html。

 

后记:这算是我的第一篇技术博客了,之前一直在有道云笔记记录自己程序员生涯遇到的点滴。然而我并不是一个太能持之以恒的人,所以决定开始写博客来督促自己,让自己成长起来。第一篇挑了一个比较简单的,但是花了很长时间,第一次写,不知道怎么样,还请各位不要吝啬于说出你们对我的看法,我在这里先谢过各位了。下一篇我会去看LinkedList的源码,因为这二者总是会拿来比较,基础面试题也相对较多,敬请期待~