Java 集合系列之二:List基本操做 java ArrayList的序列化分析 何巧妙的使用ArrayList的Clone方法

1. Java List

1. Java List重要观点

  • Java List接口是Java Collections Framework的成员。
  • List容许您添加剧复元素。
  • List容许您拥有'null'元素。
  • List接口在Java 8中有许多默认方法,例如replaceAll,sort和spliterator。
  • 列表索引从0开始,就像数组同样。
  • List支持泛型(类型的参数化),咱们应尽量使用它。将Generics与List一块儿使用将在运行时避免ClassCastException。

2. Java列表类图

Java List接口扩展了Collection接口。Collection接口 externs Iterable接口。html

一些最经常使用的List实现类是ArrayList,LinkedList,Vector,Stack,CopyOnWriteArrayList。java

AbstractList提供了List接口的骨干实现,以减小实现List的工做量。编程

   

3. Java List方法

  • int size():获取列表中元素的数量。
  • boolean isEmpty():检查列表是否为空。
  • boolean contains(Object o):若是此列表包含指定的元素,则返回true。
  • Iterator <E> iterator():以适当的顺序返回此列表中元素的迭代器。
  • Object [] toArray():以适当的顺序返回包含此列表中全部元素的数组
  • boolean add(E e):将指定的元素追加到此列表的末尾。
  • boolean remove(Object o):今后列表中删除指定元素的第一个匹配项。
  • boolean retainAll(Collection <?> c):仅保留此列表中包含在指定集合中的元素。
  • void clear():从列表中删除全部元素。
  • E get(int index):返回列表中指定位置的元素。
  • E set(int index,E element):用指定的元素替换列表中指定位置的元素。
  • ListIterator <E> listIterator():返回列表中元素的列表迭代器。
  • List <E> subList(int fromIndex,int toIndex):返回指定fromIndex(包含)和toIndex(不包括)之间的此列表部分的视图。返回的列表由此列表支持,所以返回列表中的非结构更改将反映在此列表中,反之亦然。

在Java 8中添加到List的一些默认方法是;api

  • default void replaceAll(UnaryOperator <E>运算符):将此列表的每一个元素替换为将运算符应用于该元素的结果。
  • default void sort(Comparator <super E> c):根据指定的Comparator引起的顺序对此列表进行排序。
  • default Spliterator <E> spliterator():在此列表中的元素上建立Spliterator。

2. ArrayList

1. ArrayList 结构图

ArrayList基于数组实现,是一个动态的数组链表。可是它和Java中的数组又不同,它的容量能够自动增加,相似于C语言中动态申请内存,动态增加内存!
ArrayList继承了AbstractList,实现了RandomAccess、Cloneable和Serializable接口!数组

  • 实现了RandomAccess接口,提供了随机访问功能,实际上就是经过下标序号进行快速访问,所以查找效率高。
  • 实现了Cloneable接口,即覆盖了函数clone(),实现浅拷贝。
  • 实现了Serializable接口,支持序列化,也就意味了ArrayList可以经过序列化传输。

2. ArrayList 重要特色

  1. 本质实现:Object类型的动态的数组
  2. 线程安全:非同步的。
  3. 列表长度:ArrayList中元素个数用size记录。
  4. 扩展容量:初始化容量 = 10 ,最大容量不会超过 MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8!【Integer.MAX_VALUE = 0x7fffffff,换算成二进制: 2^31 - 1,十进制就是 :2147483647,二十一亿多。一些虚拟器须要在数组前加个 头标签,因此减去 8 。 当想要分配比 MAX_ARRAY_SIZE 大的个数就会报 OutOfMemoryError。】当ArrayList容量不足以容纳所有元素时,ArrayList会从新设置容量:JDK1.6 int newCapacity = (oldCapacity * 3) /2 + 1;   JDK1.8 int newCapacity = oldCapacity + (oldCapacity >> 1); 当容量不够时,调用ensureCapacity(int minCapacity)方法调整列表容量,每次增长元素,都要将原来的元素拷贝到一个新的数组中,使用Arrays.copyOf(elementData, newCapacity)拷贝 ,很是之耗时,也所以建议在事先能肯定元素数量的状况下,才使用ArrayList,不然建议使用LinkedList。咱们能够主动调用 ensureCapcity 来增长 ArrayList 对象的容量,这样就避免添加元素满了时扩容、挨个复制后移等消耗。
  5. 3种构造方法:1)构造一个默认初始容量为10的空列表; 2) 构造一个指定初始容量的空列表; 3) 构造一个包含指定collection的元素的列表,内部是Arrays.copyOf(elementData, size, Object[].class);。
  6. 5种存储方法:1)set(int index, E element)、2)add(E e)、3)add(int index, E element)、4)addAll(Collection<? extends E> c)、5)addAll(int index, Collection<? extends E> c) 其中3,4,5调用了 System.arraycopy()
  7. 2种删除方法:1) remove(int index) ;2)remove(Object o) 
  8. 转换成数组:toArray()方法内部调用Arrays.copyOf(),toArray(T[] a)方法内部若是是部分转换用Arrays.copyOf(),所有转换用 System.arraycopy()
  9. 遍历方法:遍历时 get 的效率要 >= 迭代器。
  10. Fail-Fast机制:ArrayList 不是同步的,因此在并发访问时,若是在迭代器迭代的同时有其余线程修改了 ArrayList, fail-fast 的迭代器 Iterator/ListIterator 会报 ConcurrentModificationException 错。所以咱们在并发环境下须要外部给 ArrayList 加个同步锁,或者直接在初始化时用 Collections.synchronizedList 方法进行包装。也可使用concurrent并发包下的CopyOnWriteArrayList类。快速失败机制经过记录modCount参数来实现。迭代器的快速失败行为应该仅用于检测 bug。
  11. 序列化:ArrayList实现java.io.Serializable的方式。当写入到输出流时,先写入“容量”,再依次写入“每个元素”;当读出输入流时,先读取“容量”,再依次读取“每个元素”。那为何ArrayList里面的elementData为何要用transient来修饰?不是由于ArrayList不能序列化和反序列化,是由于elementData里面不是全部的元素都有数据,由于容量的问题,elementData里面有一些元素是空的,这种是没有必要序列化的。ArrayList的序列化和反序列化依赖writeObject和readObject方法来实现。能够避免序列化空的元素。序列化size大小的元素。
  12. 克隆: ArrayList的本质是维护了一个Object的数组,因此克隆也是经过数组的复制实现的,属于浅复制,调用的是Arrays.copyOf()。若是你想要修改克隆后的集合,那么克隆前的也会被修改。那么就须要使用深复制。经过实现对象类的clone方法。
  13. ArrayList的实现中大量地调用了Arrays.copyof()和System.arraycopy()方法。
  14. ArrayList基于数组实现,能够经过下标索引直接查找到指定位置的元素,所以查找效率高,但每次插入或删除元素,就要大量地移动元素,插入删除元素的效率低。
  15. 在查找给定元素索引值等的方法中,源码都将该元素的值分为null和不为null两种状况处理,ArrayList中容许元素为null。
  16. indexOf和lastIndexOf 查找元素,若元素不存在,则返回-1!
  17. 适合读数据多的场合。

2. LinkedList

1. LinkedList 结构图

LinkedList是基于链表实现的,从源码能够看出是一个双向链表。除了当作链表使用外,它也能够被看成堆栈、队列或双端队列进行操做安全

LinkedList不是线程安全的,继承AbstractSequentialList实现List、Deque、Cloneable、Serializable。数据结构

  • LinkedList继承AbstractSequentialList,AbstractSequentialList 实现了get(int index)、set(int index, E element)、add(int index, E element) 和 remove(int index)这些函数。这些接口都是随机访问List的。
  • LinkedList 实现 List 接口,能对它进行队列操做。
  • LinkedList 实现 Deque 接口,即能将LinkedList看成双端队列使用。
  • LinkedList 实现了Cloneable接口,即覆盖了函数clone(),能克隆。
  • LinkedList 实现java.io.Serializable接口,这意味着LinkedList支持序列化,能经过序列化去传输。

2. LinkedList 重要特色

  1. 本质实现:底层使用一个Node数据结构,有先后两个指针,双向链表实现。
  2. 线程安全:非同步的。
  3. 列表长度:LinkedList中元素个数用size记录。
  4. 列表容量:LinkedList是基于链表实现的,所以不存在容量不足的问题,因此这里没有扩容的方法。
  5. 内存:须要更多的内存,LinkedList 每一个节点中须要多存储先后节点的信息,占用空间更多些(previous  element next)。
  6. 元素容许为null。
  7. Fail-Fast机制:同ArrayList相同。
  8. 遍历方法:全部指定位置的操做都是从头开始遍历进行的。LinkedList是基于链表实现的,所以插入删除效率高,查找效率低(虽然有一个加速动做)。源码中先将index与长度size的一半比较,若是index<size/2,就只从位置0日后遍历到位置index处,而若是index>size/2,就只从位置size往前遍历到位置index处。这样能够减小一部分没必要要的遍历,从而提升必定的效率(实际上效率仍是很低)。Arrays.copyOf() 方法:
  9. 它适合删除,插入较多的场景。

3. Vector

1. Vector 结构图

Vector 类能够实现可增加的对象数组。与数组同样,它包含可使用整数索引进行访问的组件。可是,Vector 的大小能够根据须要增大或缩小,以适应建立 Vector 后进行添加或移除项的操做。Vector 是同步的,可用于多线程。多线程

  • Vector 继承了AbstractList,实现了List;因此,它是一个队列,支持相关的添加、删除、修改、遍历等功能。
  • Vector实现了RandmoAccess接口,即提供了随机访问功能。RandmoAccess是java中用来被List实现,为List提供快速访问功能的。在Vector中,咱们便可以经过元素的序号快速获取元素对象;这就是快速随机访问。
  • Vector 实现了Cloneable接口,即实现clone()函数。它能被克隆。
  • Vector 实现Serializable接口,支持序列化。

2. Vector 重要特色

  1. 本质实现:可增加的对象数组。
  2. 线程安全:同步的,不少方法都加入了synchronized同步语句,来保证线程安全。
  3. 列表长度:elementCount表示实际元素的数量。(Vector 经过 capacity (容量) 和 capacityIncrement (增加数量) 来尽可能少的占用空间)
  4. 列表容量:Vector初始化容量是10,扩容默认2倍。capacityIncrement 容量增加系数(向量的大小大于其容量时,容量自动增长的量),ensureCapacity(int minCapacity)调用ensureCapacityHelper(int minCapacity)若是此向量的当前容量小于 minCapacity,则经过将其内部数据数组(保存在字段 elementData 中)替换为一个较大的数组来增长其容量。新数据数组的大小将为原来的大小加上 capacityIncrement,若是容量的增量小于等于零,则每次须要增大容量时,向量的容量将增大一倍(编程原来的两倍),不过,若是此大小仍然小于 minCapacity,则新容量将为 minCapacity
  5. Fail-Fast机制:Vector 的 iterator 和 listIterator 方法所返回的迭代器是快速失败的:若是在迭代器建立后的任意时间从结构上修改了向量(经过迭代器自身的 remove 或 add 方法以外的任何其余方式),则迭代器将抛出 ConcurrentModificationException。所以,面对并发的修改,迭代器很快就彻底失败,而不是冒着在未来不肯定的时间任意发生不肯定行为的风险。
  6. Vector 的 elements 方法返回的 Enumeration 不是 快速失败的。
  7. Vector 主要用在事先不知道数组的大小,或者只是须要一个能够改变大小的数组的状况。
  8. 最好在插入大量元素前增长 vector 容量,那样能够减小从新申请内存的次数。

3. Array vs Vector

共同点:并发

  • 都是基于数组
  • 都支持随机访问
  • 默认容量都是 10
  • 都有扩容机制

区别:dom

  • Vector 出生的比较早,JDK 1.0 就出生了,ArrayList JDK 1.2 才出来
  • Vector 比 ArrayList 多一种迭代器 Enumeration
  • Vector 是线程安全的,ArrayList 不是
  • Vector 默认扩容 2 倍,ArrayList 是 1.5

若是没有线程安全的需求,通常推荐使用 ArrayList,而不是 Vector,由于每次都要获取锁,效率过低。

4. Stack

1. Stack 结构图

Stack 类表示后进先出(LIFO)的对象堆栈。它经过五个操做对类 Vector 进行了扩展 ,容许将向量视为堆栈。它提供了一般的 push 和 pop 操做,以及取堆栈顶点的 peek 方法、测试堆栈是否为空的 empty 方法、在堆栈中查找项并肯定到堆栈顶距离的 search 方法。
由于它继承自Vector,那么它的实现原理是以数组实现堆栈的。若是要以链表方式实现堆栈可使用LinkedList!(由于)

2. Stack 重要特色

  1. Stack是栈。它的特性是:先进后出(FILO, First In Last Out)。
  2. Stack实际上也是经过数组去实现的。实际调用的实现方法都是Vector中的方法!
  3. push时(即,将元素推入栈中),是经过将元素追加的数组的末尾中。
  4. peek时(即,取出栈顶元素,不执行删除),是返回数组末尾的元素。
  5. pop时(即,取出栈顶元素,并将该元素从栈中删除),是取出数组末尾的元素,而后将该元素从数组中删除。
  6. 栈最大的长度取决于vector里面数组能有多长。这里vector里面最大能取到Integer.MAX_VALUE。 

5.CopyOnWriteArrayList(JUC) 

1. CopyOnWriteArrayList 结构图

CopyOnWrite容器即写时复制的容器。通俗的理解是当咱们往一个容器添加元素的时候,不直接往当前容器添加,而是先将当前容器进行Copy,复制出一个新的容器,而后新的容器里添加元素,添加完元素以后,再将原容器的引用指向新的容器。这样作的好处是咱们能够对CopyOnWrite容器进行并发的读,而不须要加锁,由于当前容器不会添加任何元素。因此CopyOnWrite容器也是一种读写分离的思想,读和写不一样的容器。添加的时候是须要加锁的.

2. CopyOnWriteArrayList重要特色

  1. 增删改都须要得到锁,而且锁只有一把,而读操做不须要得到锁,支持并发。(而Vector读也须要加锁,性能差)
  2. 读的时候不须要加锁,若是读的时候有多个线程正在向CopyOnWriteArrayList添加数据,读仍是会读到旧的数据,由于写的时候不会锁住旧的CopyOnWriteArrayList。
  3. 应用:CopyOnWrite并发容器用于读多写少的并发场景。好比白名单,黑名单,商品类目的访问和更新场景。
  4. 缺点:即内存占用问题(写时复制机制->GC->应用响应时间长)和数据一致性问题(只能保证数据的最终一致性,不能保证数据的实时一致性)。
  5. CopyOnWriteArrayList 是一个线程安全的 ArrayList,经过内部的 volatile 数组和显式锁 ReentrantLock 来实现线程安全。
  6. CopyOnWriteArrayList 实现很是简单。内部使用了一个 volatile 数组(array)来存储数据,保证了多线程环境下的可见性。在更新数据时,都会新建一个数组,并将更新后的数据拷贝到新建的数组中,最后再将该数组赋值给 array。正因为这个缘由,涉及到数据更新的操做效率很低。

抄录网址

  1. https://blog.csdn.net/u010648555/column/info/14681(Java集合系列专栏)
  2. https://blog.csdn.net/ns_code/article/category/2362915(Java集合源码剖析)
  3. http://www.cnblogs.com/skywang12345/p/3323085.html( Java 集合系列)
  4. http://ifeve.com/talk-concurrency/(聊聊并发系列)
  5. Java List集合深刻学习
  6. java集合系列——List集合之ArrayList介绍(二)
  7. 深刻Java集合学习系列:ArrayList的实现原理
  8. java集合入门和深刻学习,看这篇就差很少了
  9. ArrayList 源码分析 -- 扩容问题及序列化问题
  10. 【Java集合源码剖析】ArrayList源码剖析
  11. ArrayList的elementData为何要用transient修饰
  12. java ArrayList的序列化分析
  13. 何巧妙的使用ArrayList的Clone方法
  14. Java 集合深刻理解(11):LinkedList
  15. java集合系列——List集合之LinkedList介绍(三)
  16. 【Java集合源码剖析】LinkedList源码剖析
  17. LinkedList API
  18. 聊聊并发-Java中的Copy-On-Write容器
相关文章
相关标签/搜索