Java容器类是java提供的工具包,包含了经常使用的数据结构:集合、链表、队列、栈、数组、映射等。
Java容器主要能够划分为4个部分:List列表、Set集合、Map映射、工具类(Iterator迭代器、Enumeration枚举类、Arrays和Collections)java
经过上图,能够把握两个基本主体,即Collection和Map。node
public interface Collection<E> extends Iterable<E> {}
复制代码
它是一个接口,是高度抽象出来的集合,它包含了集合的基本操做:添加、删除、清空、遍历(读取)、是否为空、获取大小、是否保护某元素等等。
在Java中全部实现了Collection接口的类都必须提供两套标准的构造函数,一个是无参,用于建立一个空的Collection,一个是带有Collection参数的有参构造函数,用于建立一个新的Collection,这个新的Collection与传入进来的Collection具有相同的元素。
例如ArrayList:数组
public ArrayList() {
throw new RuntimeException("Stub!");
}
public ArrayList(Collection<? extends E> c) {
throw new RuntimeException("Stub!");
}
复制代码
public interface List<E> extends Collection<E> {}
List是一个继承于Collection的接口,List是集合的一种。List是有序的队列,List中每个元素都有一个索引;第一个元素索引值是0,日后就依次+1,List中容许有重复的元素。 既然List是继承于Collection接口,它天然就包含了Collection中的所有函数接口;因为List是有序队列,它也额外的有本身的API接口。主要有“添加、删除、获取、修改指定位置的元素”、“获取List中的子队列”等。安全
// Collection的API
abstract boolean add(E object)
abstract boolean addAll(Collection<? extends E> collection)
abstract void clear()
abstract boolean contains(Object object)
abstract boolean containsAll(Collection<?> collection)
abstract boolean equals(Object object)
abstract int hashCode()
abstract boolean isEmpty()
abstract Iterator<E> iterator()
abstract boolean remove(Object object)
abstract boolean removeAll(Collection<?> collection)
abstract boolean retainAll(Collection<?> collection)
abstract int size()
abstract <T> T[] toArray(T[] array)
abstract Object[] toArray()
// 相比与Collection,List新增的API:
abstract void add(int location, E object)
abstract boolean addAll(int location, Collection<? extends E> collection)
abstract E get(int location)
abstract int indexOf(Object object)
abstract int lastIndexOf(Object object)
abstract ListIterator<E> listIterator(int location)
abstract ListIterator<E> listIterator()
abstract E remove(int location)
abstract E set(int location, E object)
abstract List<E> subList(int start, int end)
复制代码
实现List接口的集合主要有:ArrayList、LinkedList、Vector、Stack。bash
public class ArrayList<E> extends AbstractList<E> implements List<E>,
RandomAccess, Cloneable, Serializable {}
复制代码
ArrayList 是一个数组队列,至关于动态数组。与Java中的数组相比,它的容量能动态增加。它继承于AbstractList,实现了List, RandomAccess, Cloneable, java.io.Serializable这些接口。数据结构
- RandmoAccess为List提供快速访问功能。在ArrayList中,咱们便可以经过元素的序号快速获取元素对象,这就是快速随机访问。
- ArrayList中的操做不是线程安全的,因此为了防止意外的非同步访问,最好在建立时声明:
List list = Collections.synchronizedList(new ArrayList(...));
ArrayList有七个字段加一个定义在AbstractList的modCount:多线程
private static final long serialVersionUID = 8683452581122892189L;
private static final int DEFAULT_CAPACITY = 10;
private static final Object[] EMPTY_ELEMENTDATA = {};
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
// Android-note: Also accessed from java.util.Collections
transient Object[] elementData; // non-private to simplify nested class access
private int size;
/**
* The maximum size of array to allocate.
* Some VMs reserve some header words in an array.
* Attempts to allocate larger arrays may result in
* OutOfMemoryError: Requested array size exceeds VM limit
*/
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
protected transient int modCount = 0;
复制代码
ArrayList的默认容量DEFAULT_CAPACITY
为10,EMPTY_ELEMENTDATA
和 DEFAULTCAPACITY_EMPTY_ELEMENTDATA
是两个常量。dom
// 默认构造函数
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
// initialCapacity是ArrayList的默认容量大小。当因为增长数据致使容量不足时,容量会添加上一次容量大小的一半。
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);
}
}
// 建立一个包含collection的ArrayList
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
if ((size = elementData.length) != 0) {
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// replace with empty array.
this.elementData = EMPTY_ELEMENTDATA;
}
}
复制代码
当使用有参构造函数,而且initialCapacity
为0或者Colletion中没有元素的时候,返回的就是EMPTY_ELEMENTDATA
。当使用默认构造函数publicArrayList(),返回DEFAULTCAPACITY_EMPTY_ELEMENTDATA
。 这两个数组都是空的并不会存放值。当第一次往ArrayList添加元素的时候,实际上是将元素存放到elementData中,因此真正用来存放元素的是elementData。
add方法:函数
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
public void add(int index, E element) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
ensureCapacityInternal(size + 1); // Increments modCount!!
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}
public boolean addAll(Collection<? extends E> c) {
Object[] a = c.toArray();
int numNew = a.length;
ensureCapacityInternal(size + numNew); // Increments modCount
System.arraycopy(a, 0, elementData, size, numNew);
size += numNew;
return numNew != 0;
}
public boolean addAll(int index, Collection<? extends E> c) {
rangeCheckForAdd(index); //判断索引位置是否正确
Object[] a = c.toArray();
int numNew = a.length;
ensureCapacityInternal(size + numNew); // Increments modCount
//将ArrayList容器从index开始的全部元素向右移动到index+numNew的位置,从而腾出numNew长度的空间放c
int numMoved = size - index;
if (numMoved > 0)
System.arraycopy(elementData, index, elementData, index + numNew,numMoved);
System.arraycopy(a, 0, elementData, index, numNew);
size += numNew;
return numNew != 0;
}
复制代码
add(E e)
将元素直接添加到列表的尾部。另外3种经过System.arraycopy()
将数组进行拷贝。
add(int index, E element)
经过将index的位置空出来,进行数组数据的右移,这是很是麻烦和耗时的,因此若是指定的数据集合须要进行大量插入(中间插入)操做,须要考虑性能的消耗。
addAll(Collection<? extends E> c)
按照指定 collection 的迭代器返回的元素顺序,将该 collection 中的全部元素添加到此列表的尾部。
addAll(int index, Collection<? extends E> c)
从指定的位置开始,将指定 collection 中的全部元素插入到此列表中。
remove方法:工具
public E remove(int index) {
rangeCheck(index);
modCount++;
E oldValue = elementData(index);
//向左移的位数,下标从0开始,须要再多减1
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
//置空最后一个元素
elementData[--size] = null; // clear to let GC do its work
return oldValue;
}
public boolean remove(Object o) {
if (o == null) {
for (int index = 0; index < size; index++)
if (elementData[index] == null) {
fastRemove(index);
return true;
}
} else {
for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) {
//fastRemove()方法用于移除指定位置的元素,和remove方法相似,区别是void类型
fastRemove(index);
return true;
}
}
return false;
}
protected void removeRange(int fromIndex, int toIndex) {
modCount++;
int numMoved = size - toIndex;
System.arraycopy(elementData, toIndex, elementData, fromIndex,numMoved);
// clear to let GC do its work
int newSize = size - (toIndex-fromIndex);
for (int i = newSize; i < size; i++) {
elementData[i] = null;
}
size = newSize;
}
public boolean removeAll(Collection<?> c) {
//Checks that the specified object reference is not null
Objects.requireNonNull(c);
//false是移除相同元素,方法retainAll中置为true,是保留相同元素
return batchRemove(c, false);
}
private boolean batchRemove(Collection<?> c, boolean complement) {
final Object[] elementData = this.elementData;
int r = 0, w = 0;
boolean modified = false;
try {
for (; r < size; r++)
if (c.contains(elementData[r]) == complement)
elementData[w++] = elementData[r];
} finally {
// Preserve behavioral compatibility with AbstractCollection,
// even if c.contains() throws.
if (r != size) {
System.arraycopy(elementData, r, elementData, w, size - r);
w += size - r;
}
if (w != size) {
// clear to let GC do its work
for (int i = w; i < size; i++)
elementData[i] = null;
modCount += size - w;
size = w;
modified = true;
}
}
return modified;
}
复制代码
扩容
public void ensureCapacity(int minCapacity) {
int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
? 0: DEFAULT_CAPACITY;
if (minCapacity > minExpand) {
ensureExplicitCapacity(minCapacity);
}
}
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
复制代码
ArrayList每次新增元素时都会须要进行容量检测判断,若新增元素后元素的个数会超过ArrayList的容量,就会进行扩容操做来知足新增元素的需求。因此当咱们清楚知道业务数据量或者须要插入大量元素前,可使用ensureCapacity来手动增长ArrayList实例的容量,以减小递增式再分配的数量。
迭代效率
public static void loopOfFor(List<Integer> list){
int value;
int size = list.size();
// 基本的for
for (int i = 0; i < size; i++)
{
value = list.get(i);
}
}
/**
* 使用forecah方法遍历数组
* @param list
*/
public static void loopOfForeach(List<Integer> list){
int value;
// foreach
for (Integer integer : list)
{
value = integer;
}
}
/**
* 经过迭代器方式遍历数组
* @param list
*/
public static void loopOfIterator(List<Integer> list){
int value;
// iterator
for (Iterator<Integer> iterator = list.iterator(); iterator.hasNext();)
{
value = iterator.next();
}
}
复制代码
在遍历ArrayList中,效率最高的是loopOfFor,loopOfForeach和loopOfIterator之间关系不明确,但在增大运行次数时,loopOfIterator效率高于loopOfForeach。
public class LinkedList<E> extends AbstractSequentialList<E> implements List<E>,
Deque<E>, Cloneable, Serializable {}
复制代码
LinkedList继承于AbstractSequentialList,实现了List, Deque, Cloneable, java.io.Serializable这些接口。
AbstractSequentialList继承AbstractList,在功能上,最大限度地减小了实现受“连续访问”数据存储所需的工做。
简单的说是你的列表须要快速的添加删除数据等,用此抽象类,如果须要快速随机的访问数据等用AbstractList抽象类。
同ArrayList同样,LinkedList中的操做不是线程安全的,因此为了防止意外的非同步访问,最好在建立时声明: List list = Collections.synchronizedList(new LinkedList(...));
LinkedList实现了一个双向列表,由first字段和last字段指向列表的头部和尾部。列表的每一个节点是一个Node对象。
private static class Node<E> {
E item;
Node<E> next;
Node<E> prev;
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
复制代码
// 默认构造函数:建立一个空的链表
public LinkedList() {
header.next = header.previous = header;
}
// 包含“集合”的构造函数:建立一个包含“集合”的LinkedList
public LinkedList(Collection<? extends E> c) {
this();
addAll(c);
}
public boolean addAll(Collection<? extends E> c) {
return addAll(size, c);
}
public boolean addAll(int index, Collection<? extends E> c) {
//若插入的位置小于0或者大于链表长度,则抛出IndexOutOfBoundsException异常
checkPositionIndex(index);
Object[] a = c.toArray();
int numNew = a.length;//插入元素个数
if (numNew == 0)
return false;
Node<E> pred, succ; //定义前导与后继
if (index == size) { //若是在队尾插入
succ = null; //后继置空
pred = last; //前导指向队尾元素last
} else { //在指定位置插入
succ = node(index); //后继指向该位置
pred = succ.prev; //先导指向前一个元素
}
for (Object o : a) {
@SuppressWarnings("unchecked") E e = (E) o;
Node<E> newNode = new Node<>(pred, e, null);//建立一个新节点,指定先导,后继置空
if (pred == null)//若是先导不存在
first = newNode;//表头first指向此节点
else
pred.next = newNode;//先导存在,则将其next指向新节点
pred = newNode;//先导移动,继续建立新节点
}
if (succ == null) {
last = pred;
} else {
pred.next = succ;
succ.prev = pred;
}
size += numNew;
modCount++;
return true;
}
复制代码
LinkedList提供了一系列API用于插入和删除元素。 例linkFirst()
,linkLast()
,linkBefore()
, unlinkFirst()
,unlinkLast()
,unlink()
。
在get、set、add、remove方法中都用到了一个 node方法,它将输入的index与链表长度的1/2进行对比,小于则从first开始操做,不然从last开始操做,节省通常的查找时间。
/**
* Returns the (non-null) Node at the specified element index.
*/
Node<E> node(int index) {
// assert isElementIndex(index);
if (index < (size >> 1)) {
Node<E> x = first;
for (int i = 0; i < index; i++)
x = x.next;
return x;
} else {
Node<E> x = last;
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}
复制代码
LinkedList经过代价较低在List中间进行插入和移除,提供了优化的顺序访问,可是在随机访问方面相对较慢。
上面都提到了ArrayList、LinkedList都是非线程安全的,面对多线程对操做时,可能会产生的fail-fast事件,抛出异常java.util.ConcurrentModificationException。而ConcurrentModificationException是在操做Iterator时抛出的异常。Iterator里定义了一个叫expectedModCount
的变量,初始化等于modCount
的值。从ArrayList源码能够看到各类操做都会修改modCount
的值。 解决方案用CopyOnWriteArrayList
代替ArrayList
//CopyOnWriteArrayList
public boolean add(E e) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray(); //copy一份原来的array
int len = elements.length;
Object[] newElements = Arrays.copyOf(elements, len + 1);
newElements[len] = e; //在copy的数组上add
setArray(newElements); //原有引用指向修改后的数据
return true;
} finally {
lock.unlock();
}
}
复制代码
CopyOnWriteArrayList
在各类操做中都是先copy一份原来的array,而后操做,最后将原有的数据引用指向修改后的数据。
public class Vector extends AbstractListimplements List, RandomAccess, Cloneable,
java.io.Serializable{}
复制代码
与ArrayList类似,可是Vector是同步的。因此说Vector是线程安全的动态数组。它的操做与ArrayList几乎同样。
Vector,ArrayList与LinkedList区别,应用场景是什么?