容器是java提供的一些列的数据结构,也能够叫语法糖。容器就是用来装在其余类型数据的数据结构。java
ArrayList是数组列表因此他继承了数组的优缺点。同时他也是泛型容器能够自定义各类数据解构、对象容纳在其中。数组
父类安全
AbstractList数据结构
接口并发
List
Collection函数
RandomAccess源码分析
Cloneablethis
Serializable线程
ArrayList<Integer> arrList = new ArrayList<Integer>(); 表示建立一个支持整数类型的List集合。
arrList.add(10); arrList.add(5, 10); 第一个表示在arrList的尾部添加元素10; 第二个表示在arrList的index = 5的地方添加元素为10,而且会把5以后的数据所有右移
arrList.get(int) 获取指定位置的值
arrList.remove(int index) arrList.remove(Object object) 第一种是删除指定位置的元素,删除后该位置以后的元素所有左移动 第二种是按指定对象删除,一样的删除后再其末尾的数据要左移动
若是ArrayList 的类型为Integer, 同时又想按对象删除元素是须要把其转换成对象: arrList.remove((Integer)10).
ArrayList.class: public ArrayList(int paramInt) { if (paramInt < 0) { throw new IllegalArgumentException("Illegal Capacity: " + paramInt); } elementData = new Object[paramInt]; } public ArrayList() { this(10); } public ArrayList(Collection<? extends E> paramCollection) { elementData = paramCollection.toArray(); size = elementData.length; if (elementData.getClass() != Object[].class) { elementData = Arrays.copyOf(elementData, size, Object[].class); } }
如上能够看出来,ArrayList是一个Object对象数组结构的,在没有被指定数组长度的状况下默认是为10。Object是全部对象的父类,因此ArrayList支持全部的对象类型。
ArrayList.class
public boolean add(E paramE) { ensureCapacityInternal(size + 1); // 确保本来的数组容量还能不能容纳新元素, 会被直接按当前大小的1/2 扩大。 elementData[(size++)] = paramE; // 在尾部添加元素,同时让size + 1 return true; } public void add(int paramInt, E paramE) { rangeCheckForAdd(paramInt); // 判断paramInt会不会越界,必需要小于list的长度才能被插入,不然会报异常:IndexOutOfBoundsException(outOfBoundsMsg(paramInt)) ensureCapacityInternal(size + 1); // 确保长度是足够的。 System.arraycopy(elementData, paramInt, elementData, paramInt + 1, size - paramInt); elementData[paramInt] = paramE; size += 1; } private void ensureCapacityInternal(int paramInt) { modCount += 1; // 新长度未超过最大list的size if (paramInt - elementData.length > 0) { grow(paramInt); // 对旧的list扩大size } } private void grow(int paramInt) { int i = elementData.length; int j = i + (i >> 1); // 等价于 (int)i+i/2, 用位运算效率更高。把长度扩充到原来的1.5倍 if (j - paramInt < 0) { // 若是扩充的元素仍是小于新的list的长度,则直接去新的list的size进程扩充 j = paramInt; } if (j - 2147483639 > 0) { // (其实这里插入有可能失败了,由于若是当j = Integer.MAX_VALUE时是没有真正的扩充本来的list) j = hugeCapacity(paramInt); } // 跟系统从新分配一块数组空间,并完成拷贝工做 elementData = Arrays.copyOf(elementData, j); } private static int hugeCapacity(int paramInt) { if (paramInt < 0) { throw new OutOfMemoryError(); } // Integer.MAX_VALUE = 2147483647, 2^31 -1 return paramInt > 2147483639 ? Integer.MAX_VALUE : 2147483639; }
Array.class
public static <T> T[] copyOf(T[] paramArrayOfT, int paramInt) { return (Object[])copyOf(paramArrayOfT, paramInt, paramArrayOfT.getClass()); } public static <T, U> T[] copyOf(U[] paramArrayOfU, int paramInt, Class<? extends T[]> paramClass) { Object[] arrayOfObject = paramClass == Object[].class ? (Object[])new Object[paramInt] : // 从新分配数组 (Object[])Array.newInstance(paramClass.getComponentType(), paramInt); System.arraycopy(paramArrayOfU, 0, arrayOfObject, 0, Math.min(paramArrayOfU.length, paramInt)); // 拷贝数据 return arrayOfObject; // 新的数组地址 }
从这块的代码分析过来,能够发现有以下两种行为:
频繁内存申请和数据拷贝:
若是list频繁的插入数据会让这个list不断的从新分配数组空间,并从新拷贝数据。这个时候就考虑给一个不错的ArrayList 数组默认长度或者考虑其余更好的更适合场景的数据容器。
线程安全、并发问题
观察add(Index, element) 发现ArrayList是非线程安全的,好比线程1 缸取Index的元素,而后作修改由于获取到的是元素的应用,因此修改Index也就修改了ArrayList的值了。在这个期间若是有线程2去把Index的元素给替换了,那么线程1的元素操做就被覆盖了。
ArrayList.class
public E get(int paramInt) { rangeCheck(paramInt); // 检查位置的合法性 return (E)elementData(paramInt); // 取出对应值 } private void rangeCheck(int paramInt) { if (paramInt >= size) { throw new IndexOutOfBoundsException(outOfBoundsMsg(paramInt)); } } // 从这里也能够看出来,就是按数组的形式来操做的 E elementData(int paramInt) { return (E)elementData[paramInt]; }
这块代码浅显易懂就不作论述
ArrayList.class
// 根据指定位置删除元素 public E remove(int paramInt) { rangeCheck(paramInt); // 检查删除的元素的位置释放是合法的 modCount += 1; Object localObject = elementData(paramInt); // 取出对应位置的元素 int i = size - paramInt - 1; if (i > 0) { System.arraycopy(elementData, paramInt + 1, elementData, paramInt, i); } elementData[(--size)] = null; // 对应位置设置为null值 return (E)localObject; } // 根据对象主动删除元素 public boolean remove(Object paramObject) { int i; if (paramObject == null) { for (i = 0; i < size; i++) { if (elementData[i] == null) { fastRemove(i); return true; } } } else { for (i = 0; i < size; i++) { if (paramObject.equals(elementData[i])) { fastRemove(i); return true; } } } return false; } // 具体的删除操做 private void fastRemove(int paramInt) { modCount += 1; int i = size - paramInt - 1; if (i > 0) { System.arraycopy(elementData, paramInt + 1, elementData, paramInt, i); } elementData[(--size)] = null; } // 检查元素合法性 private void rangeCheck(int paramInt) { if (paramInt >= size) { throw new IndexOutOfBoundsException(outOfBoundsMsg(paramInt)); } }
从代码中能够得出以下结论:
null值
ArrayList中容许直接存储null值的
线程安全、并发问题
由于他是直接对ArrayList对应位置置为null,若是有多个线程访问可能存在数据安全性问题。(跟上述说的问题是同样的)
内存问题
上面说到他的内存随着数据不断插入会不断的去申请内存块,可是从这里的这块代码中能够发现,若是内存已经被分配的状况下是不会随着元素的递减(删除)而收缩内存的。
按对象删除元素
若是是按对象从ArrayList中删除元素,会从开始位置找到第一个跟删除对象匹配的值并删除,若是ArrayList中存在多个相同的对象时须要考虑清楚是不是删除你想删除的对象哦。
优势:读取速度快
缺点:插入慢,非线程安全
在使用ArrayList的时候,脑海里必须清晰好本身的场景是否会涉及到并发问题。其次要清晰的了解到数组的优缺点。由于它就是数组的实现