前面一章节,我们介绍了集合的类图,那么本节将学习Collection 接口中最常用的子类ArrayList类,本章分为下面几部分讲解(说明本章采用的JDK1.6源码进行分析,因为个人认为虽然JDK1.8进行了部分改动,但万变不离其宗,仍然采用的JDK1.6的引子进行的优化,因此学会了1.6对于1.8也就理解了)。

一、ArrayList 的常见功能

在分析ArrayList的源码前,我们先看下ArrayList的常见的功能:

  1. package study.collection;
  2.  
  3. import java.util.ArrayList;
  4. import java.util.Date;
  5. import java.util.List;
  6.  
  7. public class TestDemo01
  8. {
  9. public static void main(String[] args)
  10. {
  11. List list = new ArrayList();
  12. //ArrayList:底层实现时数组,线程不安全,效率高。所以,查询快。修改、插入、删除慢。
  13. //LinkedList:底层实现是链表,线程不安全,效率高。所以,查询慢。修改、插入、删除快。
  14. //Vector:线程安全的,效率低。
  15.  
  16. list.add("aaa");
  17. list.add("aaa");
  18. list.add(new Date());
  19. list.add(new Dog());
  20. list.add(1234); //注意,list集合中只能添加引用类型,这里包装类的:自动装箱!
  21. list.remove(new String("aaa"));
  22. System.out.println(list.size());
  23. for(int i=0;i<list.size();i++){
  24. System.out.println(list.get(i));
  25. }
  26.  
  27. list.set(3, new String("3333"));
  28. list.add(4, new String("3333"));
  29.  
  30. System.out.println(list.isEmpty());
  31. list.remove(new Dog()); //hashcode和equals
  32. System.out.println(list.size());
  33.  
  34. List list2 = new ArrayList();
  35. list2.add("bbb");
  36. list2.add("ccc");
  37.  
  38. list.add(list2);
  39.  
  40. //跟顺序的操作
  41. String str = (String) list.get(0);
  42. System.out.println(str);
  43. list.set(1, "ababa");
  44. list.remove(0);
  45. }
  46.  
  47. }
  48.  
  49. class Dog
  50. {
  51. }

从上述可以看到了,ArrayList 接口中除了继承自父类Collection 接口中的方法外,还实现了List接口中扩充的和索引相关的方法,这都源于其底层为数组结构。

二、ArrayList 的重要的属性

上面的部分列举了ArrayList中常见的一些功能方法,那么这些方法又是如何使用的呢,下面我们将进行源码的剖析,在剖析前,我们可以自己思考下,我们知道ArrayList 是一个动态扩展的集合,之所以动态扩展的原因或者说比数组强的地方肯定就在于数组的长度是固定的,不能扩展,这是数组的最大缺陷,所以才有了集合,那么ArrayList,那么其底层肯定也采用的是数组结构,因为它叫ArrayList嘛,那么其重要的属性之一,必然是定义了一个数组。如下:

  1. public class ArrayList<E> extends AbstractList<E>
  2. implements List<E>, RandomAccess, Cloneable, java.io.Serializable
  3. {
  4. private static final long serialVersionUID = 8683452581122892189L;
  5.  
  6. /**
  7. * The array buffer into which the elements of the ArrayList are stored.
  8. * The capacity of the ArrayList is the length of this array buffer.
  9. */
  10. private transient Object[] elementData;
  11.  
  12. /**
  13. * The size of the ArrayList (the number of elements it contains).
  14. *
  15. * @serial
  16. */
  17. private int size;

ArrayList就是一个以数组形式实现的集合,其元素的功能为:

private transient Object[] elementData;    //ArrayList是基于数组的一个实现,elementData就是底层的数组

private int size;   //ArrayList里面元素的个数,这里要注意一下,size是按照调用add、remove方法的次数进行自增或者自减的,所以add了一个null进入ArrayList,size也会加1

三、ArrayList 的构造方法分析

在分析完上面的属性后,我们紧接着来看下ArrayList的构造方法:

  1. /**
  2. *构造一个具有指定容量的list
  3. */
  4. public ArrayList(int initialCapacity) {
  5. super();
  6. if (initialCapacity < 0)
  7. throw new IllegalArgumentException("Illegal Capacity: "+
  8. initialCapacity);
  9. this.elementData = new Object[initialCapacity];
  10. }
  11.  
  12. /**
  13. * 构造一个初始容量为10的list,也就说当我们经常采用new ArrayList()的时候,实际分配的大小就为10
  14. */
  15. public ArrayList() {
  16. this(10);
  17. }
  18.  
  19. /**
  20. *构造一个包含指定元素的list,这些元素的是按照Collection的迭代器返回的顺序排列的
  21. */
  22. public ArrayList(Collection<? extends E> c) {
  23. elementData = c.toArray();
  24. size = elementData.length;
  25. // c.toArray might (incorrectly) not return Object[] (see 6260652) 这里是因为toArray可能不一定是Object类型的,因此判断如果不是就进行了拷贝转换操作
  26. if (elementData.getClass() != Object[].class)
  27. elementData = Arrays.copyOf(elementData, size, Object[].class);
  28. }

可以看到,ArrayList 提供了三个构造方法,分别的含义,已经注释到代码上面了,那么想一下指定容量的构造方法的意义,既然默认为10就可以那么为什么还要提供一个可以指定容量大小的构造方法呢?思考下,下面会说到。

四、ArrayList 的常见方法分析

1.add 添加元素

添加的方法,共有四个,下面我们分别分析下其功能源码:

  1. /**
  2. *添加一个元素
  3. */
  4. public boolean add(E e)
  5. {
  6. // 进行扩容检查
  7. ensureCapacity(size + 1); // Increments modCount!!
  8. //将e增加至list的数据尾部,容量+1
  9. elementData[size++] = e;
  10. return true;
  11. }
  12.  
  13. /**
  14. *在指定位置添加一个元素
  15. */
  16. public void add(int index, E element) {
  17. // 判断索引是否越界
  18. if (index > size || index < 0)
  19. throw new IndexOutOfBoundsException(
  20. "Index: "+index+", Size: "+size);
  21. // 进行扩容检查
  22. ensureCapacity(size+1); // Increments modCount!!
  23. // 对数组进行复制处理,目的就是空出index的位置插入element,并将index后的元素位移一个位置
  24. System.arraycopy(elementData, index, elementData, index + 1,
  25. size - index);
  26. // 将指定的index位置赋值为element
  27. elementData[index] = element;
  28. // list容量+1
  29. size++;
  30. }
  31.  
  32. /**
  33. *增加一个集合元素
  34. */
  35. public boolean addAll(Collection<? extends E> c) {
  36. //将c转换为数组
  37. Object[] a = c.toArray();
  38. int numNew = a.length;
  39. //扩容检查
  40. ensureCapacity(size + numNew); // Increments modCount
  41. //将c添加至list的数据尾部
  42. System.arraycopy(a, 0, elementData, size, numNew);
  43. //更新当前容器大小
  44. size += numNew;
  45. return numNew != 0;
  46. }
  47.  
  48. /**
  49. * 在指定位置,增加一个集合元素
  50. */
  51. public boolean addAll(int index, Collection<? extends E> c) {
  52. if (index > size || index < 0)
  53. throw new IndexOutOfBoundsException(
  54. "Index: " + index + ", Size: " + size);
  55.  
  56. Object[] a = c.toArray();
  57. int numNew = a.length;
  58. ensureCapacity(size + numNew); // Increments modCount
  59.  
  60. // 计算需要移动的长度(index之后的元素个数)
  61. int numMoved = size - index;
  62. // 数组复制,空出第index到index+numNum的位置,即将数组index后的元素向右移动numNum个位置
  63. if (numMoved > 0)
  64. System.arraycopy(elementData, index, elementData, index + numNew,
  65. numMoved);
  66. // 将要插入的集合元素复制到数组空出的位置中
  67. System.arraycopy(a, 0, elementData, index, numNew);
  68. size += numNew;
  69. return numNew != 0;
  70. }
  71.  
  72. /**
  73. * 数组容量检查,不够时则进行扩容
  74. */
  75. public void ensureCapacity( int minCapacity) {
  76. modCount++;
  77. // 当前数组的长度
  78. int oldCapacity = elementData.length;
  79. // 最小需要的容量大于当前数组的长度则进行扩容
  80. if (minCapacity > oldCapacity) {
  81. Object oldData[] = elementData;
  82. // 新扩容的数组长度为旧容量的1.5倍+1
  83. int newCapacity = (oldCapacity * 3)/2 + 1;
  84. // 如果新扩容的数组长度还是比最小需要的容量小,则以最小需要的容量为长度进行扩容
  85. if (newCapacity < minCapacity)
  86. newCapacity = minCapacity;
  87. // minCapacity is usually close to size, so this is a win:
  88. // 进行数据拷贝,Arrays.copyOf底层实现是System.arrayCopy()
  89. elementData = Arrays.copyOf(elementData, newCapacity);
  90. }
  91. }

2.remove 删除

  1. /**
  2. * 根据索引位置删除元素
  3. */
  4. public E remove(int index) {
  5. // 数组越界检查
  6. RangeCheck(index);
  7.  
  8. modCount++;
  9. // 取出要删除位置的元素,供返回使用
  10. E oldValue = (E) elementData[index];
  11. // 计算数组要复制的数量
  12. int numMoved = size - index - 1;
  13. // 数组复制,就是将index之后的元素往前移动一个位置
  14. if (numMoved > 0)
  15. System. arraycopy(elementData, index+1, elementData, index,
  16. numMoved);
  17. // 将数组最后一个元素置空(因为删除了一个元素,然后index后面的元素都向前移动了,所以最后一个就没用了),好让gc尽快回收
  18. // 不要忘了size减一
  19. elementData[--size ] = null; // Let gc do its work
  20.  
  21. return oldValue;
  22. }
  23.  
  24. /**
  25. * 根据元素内容删除,只删除匹配的第一个
  26. */
  27. public boolean remove(Object o) {
  28. // 对要删除的元素进行null判断
  29. // 对数据元素进行遍历查找,知道找到第一个要删除的元素,删除后进行返回,如果要删除的元素正好是最后一个那就惨了,时间复杂度可达O(n) 。。。
  30. if (o == null) {
  31. for (int index = 0; index < size; index++)
  32. // null值要用==比较
  33. if (elementData [index] == null) {
  34. fastRemove(index);
  35. return true;
  36. }
  37. } else {
  38. for (int index = 0; index < size; index++)
  39. // 非null当然是用equals比较了
  40. if (o.equals(elementData [index])) {
  41. fastRemove(index);
  42. return true;
  43. }
  44. }
  45. return false;
  46. }
  47.  
  48. /*
  49. * Private remove method that skips bounds checking and does not
  50. * return the value removed.
  51. */
  52. private void fastRemove(int index) {
  53. modCount++;
  54. // 原理和之前的add一样,还是进行数组复制,将index后的元素向前移动一个位置,不细解释了,
  55. int numMoved = size - index - 1;
  56. if (numMoved > 0)
  57. System. arraycopy(elementData, index+1, elementData, index,
  58. numMoved);
  59. elementData[--size ] = null; // Let gc do its work
  60. }
  61.  
  62. /**
  63. * 数组越界检查
  64. */
  65. private void RangeCheck(int index) {
  66. if (index >= size )
  67. throw new IndexOutOfBoundsException(
  68. "Index: "+index+", Size: " +size);
  69. }

分析:

增加和删除方法到这里就解释完了,代码是很简单,主要需要特别关心的就两个地方:1.数组扩容,2.数组复制,这两个操作都是极费效率的,最惨的情况下(添加到list第一个位置,删除list最后一个元素或删除list第一个索引位置的元素)时间复杂度可达O(n)。

还记得上面那个坑吗(为什么提供一个可以指定容量大小的构造方法 )?看到这里是不是有点明白了呢,简单解释下:如果数组初试容量过小,假设默认的10个大小,而我们使用ArrayList的主要操作时增加元素,不断的增加,一直增加,不停的增加,会出现上面后果?那就是数组容量不断的受挑衅,数组需要不断的进行扩容,扩容的过程就是数组拷贝System.arraycopy的过程,每一次扩容就会开辟一块新的内存空间和数据的复制移动,这样势必对性能造成影响。那么在这种以写为主(写会扩容,删不会缩容)场景下,提前预知性的设置一个大容量,便可减少扩容的次数,提高了性能。

数组扩容伴随着开辟新建的内存空间以创建新数组然后进行数据复制,而数组复制不需要开辟新内存空间,只需将数据进行复制。

上面讲增加元素可能会进行扩容,而删除元素却不会进行缩容,如果在已删除为主的场景下使用list,一直不停的删除而很少进行增加,那么会出现什么情况?再或者数组进行一次大扩容后,我们后续只使用了几个空间,会出现上面情况?当然是空间浪费啦啦啦,怎么办呢?

  1. /**
  2.      * 将底层数组的容量调整为当前实际元素的大小,来释放空间。
  3.      */
  4.     public void trimToSize() {
  5.         modCount++;
  6.        // 当前数组的容量
  7.         int oldCapacity = elementData .length;
  8.        // 如果当前实际元素大小 小于 当前数组的容量,则进行缩容
  9.         if (size < oldCapacity) {
  10.             elementData = Arrays.copyOf( elementData, size );
  11.        }

3.更新 set

  1. /**
  2. * 将指定位置的元素更新为新元素
  3. */
  4. public E set( int index, E element) {
  5. // 数组越界检查
  6. RangeCheck(index);
  7.  
  8. // 取出要更新位置的元素,供返回使用
  9. E oldValue = (E) elementData[index];
  10. // 将该位置赋值为行的元素
  11. elementData[index] = element;
  12. // 返回旧元素
  13. return oldValue;
  14. }

4.查找

  1. /**
  2. * 查找指定位置上的元素
  3. */
  4. public E get( int index) {
  5. RangeCheck(index);
  6.  
  7. return (E) elementData [index];
  8. }

由于ArrayList使用数组实现,更新和查找直接基于下标操作,变得十分简单。

5.是否包含.

  1. /**
  2. * Returns <tt>true</tt> if this list contains the specified element.
  3. * More formally, returns <tt>true</tt> if and only if this list contains
  4. * at least one element <tt>e</tt> such that
  5. * <tt>(o==null ? e==null : o.equals(e))</tt>.
  6. *
  7. * @param o element whose presence in this list is to be tested
  8. * @return <tt> true</tt> if this list contains the specified element
  9. */
  10. public boolean contains(Object o) {
  11. return indexOf(o) >= 0;
  12. }
  13.  
  14. /**
  15. * Returns the index of the first occurrence of the specified element
  16. * in this list, or -1 if this list does not contain the element.
  17. * More formally, returns the lowest index <tt>i</tt> such that
  18. * <tt>(o==null ? get(i)==null : o.equals(get(i)))</tt>,
  19. * or -1 if there is no such index.
  20. */
  21. public int indexOf(Object o) {
  22. if (o == null) {
  23. for (int i = 0; i < size; i++)
  24. if (elementData [i]==null)
  25. return i;
  26. } else {
  27. for (int i = 0; i < size; i++)
  28. if (o.equals(elementData [i]))
  29. return i;
  30. }
  31. return -1;
  32. }
  33.  
  34. /**
  35. * Returns the index of the last occurrence of the specified element
  36. * in this list, or -1 if this list does not contain the element.
  37. * More formally, returns the highest index <tt>i</tt> such that
  38. * <tt>(o==null ? get(i)==null : o.equals(get(i)))</tt>,
  39. * or -1 if there is no such index.
  40. */
  41. public int lastIndexOf(Object o) {
  42. if (o == null) {
  43. for (int i = size-1; i >= 0; i--)
  44. if (elementData [i]==null)
  45. return i;
  46. } else {
  47. for (int i = size-1; i >= 0; i--)
  48. if (o.equals(elementData [i]))
  49. return i;
  50. }
  51. return -1;
  52. }

contains主要是检查indexOf,也就是元素在list中出现的索引位置也就是数组下标,再看indexOf和lastIndexOf代码是不是很熟悉,没错,和public booleanremove(Object o) 的代码一样,都是元素null判断,都是循环比较。

6.容量判断

  1. /**
  2. * Returns the number of elements in this list.
  3. *
  4. * @return the number of elements in this list
  5. */
  6. public int size() {
  7. return size ;
  8. }
  9.  
  10. /**
  11. * Returns <tt>true</tt> if this list contains no elements.
  12. *
  13. * @return <tt> true</tt> if this list contains no elements
  14. */
  15. public boolean isEmpty() {
  16. return size == 0;
  17. }

说明:modCount 是干什么的,怎么到处都在操作这个变量,还有遍历呢,为啥不讲?由于iterator遍历相对比较复杂,而且iterator 是GoF经典设计模式比较重要的一个,之后会对iterator单独分析。

五、transient 分析  和 ArrayList的优缺点

1.ArrayList的优缺点

1、ArrayList底层以数组实现,是一种随机访问模式,再加上它实现了RandomAccess接口,因此查找也就是get的时候非常快

2、ArrayList在顺序添加一个元素的时候非常方便,只是往数组里面添加了一个元素而已

不过ArrayList的缺点也十分明显:

1、删除元素的时候,涉及到一次元素复制,如果要复制的元素很多,那么就会比较耗费性能

2、插入元素的时候,涉及到一次元素复制,如果要复制的元素很多,那么就会比较耗费性能

因此,ArrayList比较适合顺序添加、随机访问的场景

2.ArrayList和Vector的区别

ArrayList是线程非安全的,这很明显,因为ArrayList中所有的方法都不是同步的,在并发下一定会出现线程安全问题。那么我们想要使用ArrayList并且让它线程安全怎么办?一个方法是用Collections.synchronizedList方法把你的ArrayList变成一个线程安全的List,比如:

  1. List<String> synchronizedList = Collections.synchronizedList(list);
  2. synchronizedList.add("aaa");
  3. synchronizedList.add("bbb");
  4. for (int i = 0; i < synchronizedList.size(); i++)
  5. {
  6. System.out.println(synchronizedList.get(i));
  7. }

另一个方法就是Vector,它是ArrayList的线程安全版本,其实现90%和ArrayList都完全一样,区别在于:

1、Vector是线程安全的,ArrayList是线程非安全的

2、Vector可以指定增长因子,如果该增长因子指定了,那么扩容的时候会每次新的数组大小会在原数组的大小基础上加上增长因子;如果不指定增长因子,那么就给原数组大小

3.为什么ArrayList的elementData是用transient修饰的?

  1. private transient Object[] elementData;

为什么elementData是使用transient修饰的呢?我们看一下ArrayList的定义:

  1. public class ArrayList<E> extends AbstractList<E>
  2. implements List<E>, RandomAccess, Cloneable, java.io.Serializable

看到ArrayList实现了Serializable接口,这意味着ArrayList是可以被序列化的,用transient修饰elementData意味着我不希望elementData数组被序列化。这是为什么?因为序列化ArrayList的时候,ArrayList里面的elementData未必是满的,比方说elementData有10的大小,但是我只用了其中的3个,那么是否有必要序列化整个elementData呢?显然没有这个必要,因此ArrayList中重写了writeObject方法:

  1. private void writeObject(java.io.ObjectOutputStream s)
  2. throws java.io.IOException{
  3. // Write out element count, and any hidden stuff
  4. int expectedModCount = modCount;
  5. s.defaultWriteObject();
  6. // Write out array length
  7. s.writeInt(elementData.length);
  8. // Write out all elements in the proper order.
  9. for (int i=0; i<size; i++)
  10. s.writeObject(elementData[i]);
  11. if (modCount != expectedModCount) {
  12. throw new ConcurrentModificationException();
  13. }
  14. }

每次序列化的时候调用这个方法,先调用defaultWriteObject()方法序列化ArrayList中的非transient元素,elementData不去序列化它,然后遍历elementData,只序列化那些有的元素,这样:

1、加快了序列化的速度

2、减小了序列化之后的文件大小

六、自己编写一个MyArrayList

  1. package study.collection;
  2.  
  3. import java.util.Arrays;
  4. import java.util.Collection;
  5. import java.util.Iterator;
  6. import java.util.List;
  7. import java.util.ListIterator;
  8.  
  9. public class MyArrayList implements List {
  10.  
  11. //底层用一个数组接收
  12. private Object[] elementData;
  13.  
  14. //用于记录集合的元素个数
  15. private int size;
  16.  
  17. /**
  18. * 无参数构造,默认为大小10
  19. */
  20. public MyArrayList()
  21. {
  22. this(10);
  23. }
  24.  
  25. /**
  26. * 初始化带容量的构造方法
  27. * @param cap
  28. */
  29. public MyArrayList(int cap)
  30. {
  31. super();
  32. if(cap < 0)
  33. {
  34. throw new IllegalArgumentException("Illegal cap: "+
  35. cap);
  36. }
  37. elementData = new Object[cap];
  38. }
  39.  
  40. @Override
  41. public boolean add(Object e) {
  42. //1.添加之前确认集合中的大小是够,因此扩容判断
  43. ensureCapacity(size +1); //添加元素一个一个添加,因此size+1;
  44. //2.填充元素
  45. elementData[size++] = e;
  46. return true;
  47. }
  48.  
  49. /**
  50. * 扩容判断,因为只要添加元素,就需要判断容器的大小是否满足
  51. * @param i
  52. */
  53. private void ensureCapacity(int minCapacity) {
  54. //扩容前,需要获取当前的数组元素的大小
  55. int oldCapacity = elementData.length;
  56. //只有当前的容量不满足,则扩容处理
  57. if(oldCapacity < minCapacity)
  58. {
  59. //新大小的比例,采用原来大小的1.5倍
  60. int newCapacity = (oldCapacity * 3)/2 + 1;
  61. //如果新算出来的大小不满足当前需要扩容的大小,则就以用户需要的为主,如果以满足则以算出来的最佳大小为主
  62. if(newCapacity < minCapacity)
  63. {
  64. newCapacity = minCapacity;
  65. }
  66. //比例算好后,开始执行数组的拷贝操作
  67. Arrays.copyOf(elementData, newCapacity,Object[].class);
  68. }
  69. }
  70.  
  71. @Override
  72. public void add(int index, Object element) {
  73. //添加指定位置,首先需要做的就是确保索引满足要求,如果要添加的索引超过了元素个数中的大小
  74. if(index > size || index < 0)
  75. {
  76. throw new IndexOutOfBoundsException(
  77. "Index: "+index+", Size: "+size);
  78. }
  79. //如果没有超过,那么就需要开始添加元素,这个时候同样需要判断扩容
  80. ensureCapacity(size +1); //添加元素一个一个添加,因此size+1;
  81. //接着需要做的事情是需要将原来位于index 位置的元素,向后面移动
  82. //首先判断,index的后面是否还有元素
  83. int modNum = size - index;
  84. if(modNum > 0)
  85. {
  86. System.arraycopy(elementData, index, elementData, index+1, size - index);
  87. }
  88. //如果没有元素或者已经拷贝完成,则直接在对应的索引处放置元素即可
  89. elementData[index] = element;
  90. //元素个数加+1
  91. size++;
  92. }
  93.  
  94. @Override
  95. public boolean addAll(Collection c) {
  96. //添加集合元素,首先需要将集合转换为数组,计算出数组的大小
  97. Object[] a = c.toArray();
  98. //计算出需要的长度
  99. int numNew = a.length;
  100. //开始扩容判断
  101. ensureCapacity(size +numNew); //添加元素的个数为numNew
  102. //开始数组拷贝
  103. System.arraycopy(a, 0, elementData, size, numNew);
  104. size += numNew;
  105. return numNew != 0;
  106. }
  107.  
  108. @Override
  109. public boolean addAll(int index, Collection c) {
  110. //首先索引的正确性
  111. if (index > size || index < 0)
  112. throw new IndexOutOfBoundsException(
  113. "Index: " + index + ", Size: " + size);
  114. //添加的元素集合转换为数组,计算要拷贝的长度,准备扩容
  115. Object[] a = c.toArray();
  116. int numNew = a.length;
  117. ensureCapacity(size + numNew); // Increments modCount
  118. //因为是指定位置扩容,因此需要判断下index后面是否有元素
  119. int numMoved = size - index;
  120. //如果大于0,说明要先空出位置来给a数组
  121. if(numMoved > 0)
  122. {
  123. System.arraycopy(elementData, index, elementData, index+1, size-index);
  124. }
  125. //空出为位置后,然后将集合的元素放到空出的位置上面
  126. System.arraycopy(a, 0, elementData,index, numNew);
  127. size += numNew;
  128. return numNew != 0;
  129. }
  130.  
  131. @Override
  132. public void clear() {
  133. for (int i = 0; i < size; i++)
  134. elementData[i] = null;
  135. size = 0;
  136. }
  137.  
  138. @Override
  139. public boolean contains(Object o) {
  140. return indexOf(o) >= 0;
  141. }
  142.  
  143. @Override
  144. public boolean containsAll(Collection c) {
  145. //迭代器暂不去实现
  146. return false;
  147. }
  148.  
  149. @Override
  150. public Object get(int index) {
  151. //对于数组而言,根据索引获取元素非常简单,但需要先检查inde的合法性,避免越界
  152. RangeCheck(index);
  153. return elementData[index];
  154. }
  155.  
  156. private void RangeCheck(int index) {
  157. if (index >= size)
  158. throw new IndexOutOfBoundsException(
  159. "Index: "+index+", Size: "+size);
  160. }
  161.  
  162. @Override
  163. public int indexOf(Object o) {
  164. //循环遍历,找出元素,注意是equals比较
  165. if (o == null) {
  166. for (int i = 0; i < size; i++)
  167. if (elementData[i]==null)
  168. return i;
  169. } else {
  170. for (int i = 0; i < size; i++)
  171. if (o.equals(elementData[i]))
  172. return i;
  173. }
  174. return -1;
  175. }
  176.  
  177. @Override
  178. public boolean isEmpty() {
  179. return size == 0;
  180. }
  181.  
  182. @Override
  183. public Iterator iterator() {
  184. //涉及迭代器,暂不去关注
  185. return null;
  186. }
  187.  
  188. @Override
  189. public int lastIndexOf(Object o) {
  190. //反向获取,则反向循环
  191. if (o == null) {
  192. for (int i = size-1; i >= 0; i--)
  193. if (elementData[i]==null)
  194. return i;
  195. } else {
  196. for (int i = size-1; i >= 0; i--)
  197. if (o.equals(elementData[i]))
  198. return i;
  199. }
  200. return -1;
  201.  
  202. }
  203.  
  204. @Override
  205. public ListIterator listIterator() {
  206. //涉及迭代器,暂不去关注
  207. return null;
  208. }
  209.  
  210. @Override
  211. public ListIterator listIterator(int index) {
  212. //涉及迭代器,暂不去关注
  213. return null;
  214. }
  215.  
  216. @Override
  217. public boolean remove(Object o) {
  218. if (o == null) {
  219. for (int index = 0; index < size; index++)
  220. if (elementData[index] == null) {
  221. fastRemove(index);
  222. return true;
  223. }
  224. } else {
  225. for (int index = 0; index < size; index++)
  226. if (o.equals(elementData[index])) {
  227. fastRemove(index);
  228. return true;
  229. }
  230. }
  231. return false;
  232. }
  233.  
  234. //找到指定的索引开始删除
  235. private void fastRemove(int index) {
  236. int numMoved = size - index - 1;
  237. if (numMoved > 0)
  238. System.arraycopy(elementData, index+1, elementData, index,
  239. numMoved);
  240. elementData[--size] = null;
  241.  
  242. }
  243.  
  244. @Override
  245. public Object remove(int index) {
  246. //合法性检查
  247. RangeCheck(index);
  248. //取出原来老的元素,以便返回
  249. Object oldValue = elementData[index];
  250. //需要开始拷贝数组,因为删除了索引处的元素,那么则需要向前移动元素
  251. //需要看后面有没有移动的元素,-1 是减去当前这个删除的元素
  252. int numMoved = size - index - 1;
  253. if (numMoved > 0)
  254. System.arraycopy(elementData, index+1, elementData, index,
  255. numMoved);//从index+1 开始拷贝到 index 处
  256. elementData[--size] = null; //元素个数减去一,同事最后一个位置置空,等待垃圾回收
  257.  
  258. return oldValue;
  259. }
  260.  
  261. @Override
  262. public boolean removeAll(Collection c) {
  263. ////涉及迭代器,暂不去关注
  264. return false;
  265. }
  266.  
  267. @Override
  268. public boolean retainAll(Collection c) {
  269. ////涉及迭代器,暂不去关注
  270. return false;
  271. }
  272.  
  273. @Override
  274. public Object set(int index, Object element) {
  275. RangeCheck(index);
  276.  
  277. Object oldValue = elementData[index];
  278. elementData[index] = element;
  279. return oldValue;
  280. }
  281.  
  282. @Override
  283. public int size() {
  284. return size;
  285. }
  286.  
  287. @Override
  288. public List subList(int fromIndex, int toIndex) {
  289. ////涉及迭代器,暂不去关注
  290. return null;
  291. }
  292.  
  293. @Override
  294. public Object[] toArray() {
  295. return Arrays.copyOf(elementData, size);
  296. }
  297.  
  298. @Override
  299. public Object[] toArray(Object[] a) {
  300. if (a.length < size)
  301. // Make a new array of a's runtime type, but my contents:
  302. return Arrays.copyOf(elementData, size, a.getClass());
  303. System.arraycopy(elementData, 0, a, 0, size);
  304. if (a.length > size)
  305. a[size] = null;
  306. return a;
  307. }
  308.  
  309. //测试
  310. public static void main(String[] args)
  311. {
  312. MyArrayList list = new MyArrayList();
  313. list.add("333");
  314. list.add("444");
  315. list.add("5");
  316. list.add("344433");
  317. list.add("333");
  318. list.add("333");
  319. System.out.println(list.size());
  320. // System.out.println(list.get(6));
  321. list.remove("444");
  322. System.out.println(list.size());
  323. }
  324. }

说明:其他关于JDK1.6 和 1.7 和 1.8 的区别可以看:

http://blog.csdn.net/u011392897/article/details/57105709

JAVA提高十:ArrayList 深入分析的更多相关文章

  1. JAVA提高十二:HashMap深入分析

    首先想说的是关于HashMap源码的分析园子里面应该有很多,并且都是分析得很不错的文章,但是我还是想写出自己的学习总结,以便加深自己的理解,因此就有了此文,另外因为小孩过来了,因此更新速度可能放缓了, ...

  2. JAVA提高十九:WeakHashMap&EnumMap&LinkedHashMap&LinkedHashSet深入分析

    因为最近工作太忙了,连续的晚上支撑和上班,因此没有精力来写下这篇博客,今天上午正好有一点空,因此来复习一下不太常用的集合体系大家族中的几个类:WeakHashMap&EnumMap&L ...

  3. Java提高十五:容器元素比较Comparable&Comparator深入分析

    我们经常用容器来存放元素,通常而言我们是不关系容器中的元素是否有序,但有些场景可能要求容器中的元素是有序的,这个时候用ArrayList  LinkedList  Hashtable HashMap ...

  4. Java提高十六:TreeMap深入分析

    上一篇容器元素比较Comparable&Comparator分析的时候,我们提到了TreeMap,但没有去细致分析它,只是说明其在添加元素的时候可以进行比较,从而使得集合有序,但是怎么做的呢? ...

  5. JAVA提高十八:Vector&Stack深入分析

    前面我们已经接触过几种数据结构了,有数组.链表.Hash表.红黑树(二叉查询树),今天再来看另外一种数据结构:栈. 什么是栈呢,我们先看一个例子:栈就相当于一个很窄的木桶,我们往木桶里放东西,往外拿东 ...

  6. JAVA提高十四:HashSet深入分析

    前面我们介绍了HashMap,Hashtable,那么还有一个hash家族,那就是HashSet;在讲解HashSet前,大家先要知道的是HashSet是单值集合的接口,即是Collection下面的 ...

  7. java提高篇---ArrayList

    一.ArrayList概述 ArrayList是实现List接口的动态数组,所谓动态就是它的大小是可变的.实现了所有可选列表操作,并允许包括 null 在内的所有元素.除了实现 List 接口外,此类 ...

  8. 【java提高】---ArrayList源码

    ArrayList源码 一.定义 public class ArrayList<E> extends AbstractList<E> implements List<E& ...

  9. Java提高合集(转载)

    转载自:http://www.cnblogs.com/pony1223/p/7643842.html Java提高十五:容器元素比较Comparable&Comparator深入分析 JAVA ...

随机推荐

  1. 多个版本的Python如何设置不冲突

    同时装有Python2.7和Python3.5说明. 问题1: 设置右键点击"Edit with IDLE"选项打开的Python版本? 在运行输入"regedit&qu ...

  2. 鸟哥Linux学习笔记03

    1, 在Linux中,默认情况下所有的系统上的账号都记录在/etc/passwd这个文件内,密码记录在/etc/shadow这个文件下,所有的组名都记录在/etc/group内,这三个文件可以说是Li ...

  3. GNU/Linux-MariaDB

    第一章 基础知识 基本术语 数据库(Database) 存储已经组织好的数据的 容器(通 常是 一个文件或者文件集) 人们经常使用术语"数据库"来指代他们运行的数据库软件,这是错 ...

  4. Tomcat代码执行漏洞(CVE-2017-12615)的演绎及个人bypass

    0x00 漏洞简介 2017年9月19日,Apache Tomcat官方确认并修复了两个高危漏洞. 漏洞CVE编号:CVE-2017-12615和CVE-2017-12616. 其中 远程代码执行漏洞 ...

  5. Unity 3D 之贪吃蛇 Text 心得 & Audio

    当我们需要在游戏街面上增加文本时, 我们就需要用到Text 组件 注意: 当文本的长度或者宽度不够时,字体将无法显示. 因为是面对组件编程,所以每一个组件的component都可以同过GetCompo ...

  6. 【SQL】- 基础知识梳理(六) - 游标

    游标的概念 结果集,结果集就是select查询之后返回的所有行数据的集合. 游标(Cursor): 是处理数据的一种方法. 它可以定位到结果集中的某一行,对数据进行读写. 也可以移动游标定位到你需要的 ...

  7. js 递归函数的使用及常用函数

    1.递归函数的使用: 公园里有一堆桃子,猴子每天吃掉一半,挑出一个坏的扔掉,第6天的时候发现还剩1个桃子,问原来有多少个桃子 var peache;function peaches(n) { if ( ...

  8. Run Away 模拟退火

    Run Away Time Limit:5000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u Submit Sta ...

  9. 【转】DSCP 与IP 优先级IP优先级

    在IPv4的报文头中,TOS字段是1字节,如下图所示.根据RFC1122的定义,IP优先级(IPPrecedence)使用最高3比特(第0-2比特).+++++++++++++++++++++++++ ...

  10. 浅析php curl_multi_*系列函数进行批量http请求

    何起: 一系列 数量很大 数据不热 还希望被蜘蛛大量抓取的页面,在蜘蛛抓取高峰时,响应时间会被拉得很高. 前人做了这样一个事儿:页面分3块,用3个内部接口提供,入口文件用curl_multi_*系列函 ...