一、ArrayList认识
ArrayList是可以动态增长和缩减的索引序列,它是基于数组实现的List类。如图
二、源码解析
内部存储元素是数组,默认数据大小是10,下面介绍常用方法。
2.1、构造方法
//第一种 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)增加元素
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)删除操作
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)更改操作
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)查操作
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();
原文:https://www.cnblogs.com/HansonYao/p/arrayList.html