数组和集合
- 数组:存储对象的一种容器(可以存储基本类型数据),数组最大的缺点就是长度固定,一不建议使用。
- 集合:类集实际上就属于动态对象数组(只能存储对象,且对象的类型可以不同),与数组相比最大的好处就是长度可以改变。
下面为Java的集合框架图
菜鸟教程
从面的集合框架图可以看出,所有的集合类都实现了Iterator接口,且Java集合主要包含两种容器:
Iterator: 此接口用于遍历集合中的元素。
主要有如下方法:
hasNext():是否存在下一个元素
next():返回下一个元素
remove():删除当前元素
集合(Collection): 存储一个元素的集合。此接口是集合类的根接口,Java中没有提供该接口的直接实现类,但是有两个接口
(List和Set)继承了该Collection接口
图(Map): 存储键值对映射。同样是属于java.util包下的另外一个接口,和Collection是相互独立的、没有关系,
Collection接口提供的方法
主要方法详解:
add():将指定的对象存储到容器中
addAll():将指定集合中的所有元素添加到此集合
clear():从此集合中删除所有元素
remove():将指定的对象在集合中删除
removeAll():将指定的集合中的元素删除
isEmpty():判断集合是否为空
contains():判断集合中是否包含指定对象
containsAll():判断集合中是否包含指定集合
equals():判断两个对象是否相等
size():返回集合容器的大小
toArray():集合转化为数组
List集合
List接口对Collection接口的方法进行了扩充:
get():根据索引取得保存数据
set():修改数据
并且List集合仍然为接口,不可以进行实例化,要想取得实例化需要有子类,
List的常用子类:ArrayList、LinkedList、Vector
List:有序的Collection、允许数据重复
ArrayList:底层是数组实现。因为是数组实现,所以在进行增加和删除元素时会涉及到整个数组的扩容以及
拷贝元素,所以速度相对较慢;而数组可以直接使用索引进行查找,速度相对较快。
LinkedList:底层是双链表实现。因为是链表实现,增加和删除元素时,只需要改变节点的指向,所以效率较高;
而进行查元素时,需要一个个进行遍历,速度相对较慢。
Vector:底层是数组实现。和ArrayList原理相同,但是是线程安全的,效率比其低。
适用场景分析:
List集合使用与顺序存储,允许元素重复出现,List接口中常用的实现类是ArrayList和LinkedList;若查询较多的话使用
ArrayList,插入和删除元素较多的haul使用LinkedList,若对线程安全有要求的话就是用Vector。
ArrayList
ArrayList就是传说中的动态数组,即是Array的复杂版本,提供有如下的好处:
(1)动态增加和减少元素
(2)实现了Collection和List接口
(3)灵活的设置数组的大小
- ArrayList的构造器:
//默认构造器,构造一个空列表,初始容量为10
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);
}
}
//用一个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;
}
}
- ArrayList的同步
在Java集合工具类Collections中,提供了一个Collections.synchonizedList方法,可以传入一个List对象,返回一个
SynchronizedList。SynchronizedList只是对List对象的封装,对List的每个操作都添加可synchonized修饰,基本上与Vector一致。
- 部分源码剖析
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
//序列版本号
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 = {};
//数组缓冲区中存储ArrayList元素的数组缓冲区。ArrayList的容量是这个数组缓冲区的长度
transient Object[] elementData; // non-private to simplify nested class access
//ArrayList的大小(它包含的元素的数量)。
private int size;
//此处省去构造函数(在前期有说明)
//将当前容量值设为实际元素的个数()
//modCount在AbstractList中定义,表示列表在结构上被修改的次数
public void trimToSize() {
modCount++;
if (size < elementData.length) {
elementData = (size == 0)
? EMPTY_ELEMENTDATA
: Arrays.copyOf(elementData, size);
}
}
//确定ArrayList的容量
//当容量不足以容纳当前全部元素,设置新的容量=(原始容量*3)/2
public void ensureCapacity(int minCapacity) {
int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
// any size if not default element table
? 0
// larger than default for default empty table. It's already
// supposed to be at default size.
: DEFAULT_CAPACITY;
if (minCapacity > minExpand) {
ensureExplicitCapacity(minCapacity);
}
}
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private void ensureExplicitCapacity(int minCapacity) {
//将修改统计数+1
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
//要分配集合的最大大小
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
//定义扩容方式。确保至少能保持
由最小容量参数指定的元素个数。
private void grow(int minCapacity) {
// overflow-conscious code
//旧容量 = 数组的长度
int oldCapacity = elementData.length;
//新的容量为旧容量+旧容量的一般(扩容方式是每次扩充原始容量的一般,即是旧容量的1.5倍)
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);
}
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
//返回列表中的元素个数
public int size() {
return size;
}
//判断集合是否为空。如果这个列表不包含任何元素,那么返回true
public boolean isEmpty() {
return size == 0;
}
//判断集合中是否包含指定元素。如果这个列表包含指定的元素,则返回true。
public boolean contains(Object o) {
return indexOf(o) >= 0;
}
//返回指定元素的第一次出现的索引,若不存在此元素返回-1
public int indexOf(Object o) {
if (o == null) {
for (int i = 0; i < size; i++)
if (elementData[i]==null)
return i;
} else {
for (int i = 0; i < size; i++)
if (o.equals(elementData[i]))
return i;
}
return -1;
}
//返回指定元素的第一次出现的索引,若不存在此元素返回-1
public int lastIndexOf(Object o) {
if (o == null) {
for (int i = size-1; i >= 0; i--)
if (elementData[i]==null)
return i;
} else {
for (int i = size-1; i >= 0; i--)
if (o.equals(elementData[i]))
return i;
}
return -1;
}
//克隆。返回这个ArrayList实例的一个浅副本。
public Object clone() {
try {
ArrayList<?> v = (ArrayList<?>) super.clone();
v.elementData = Arrays.copyOf(elementData, size);
v.modCount = 0;
return v;
} catch (CloneNotSupportedException e) {
// this shouldn't happen, since we are Cloneable
throw new InternalError(e);
}
}
//将转化为数组,按照适当的顺序(从第一个元素到最后一个元素)返回包含列表中所有元素的数组
public Object[] toArray() {
return Arrays.copyOf(elementData, size);
}
//返回指定位置的元素
public E get(int index) {
rangeCheck(index);
return elementData(index);
}
//用指定元素替换集合中指定位置的元素
public E set(int index, E element) {
rangeCheck(index);
E oldValue = elementData(index);
elementData[index] = element;
return oldValue;
}
//在集合末尾添加指定的元素
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
//在指定的位置添加指定元素
public void add(int index, E element) {
rangeCheckForAdd(index);
ensureCapacityInternal(size + 1); // Increments modCount!!
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}
//删除集合中指定位置的元素
public E remove(int index) {
rangeCheck(index);
modCount++;
E oldValue = elementData(index);
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(index);
return true;
}
}
return false;
}
//跳过边界检查的私有移除方法,
private void fastRemove(int index) {
modCount++;
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
}
//删除集合中所有的元素
public void clear() {
modCount++;
// clear to let GC do its work
for (int i = 0; i < size; i++)
elementData[i] = null;
size = 0;
}
//将指定集合的所有元素添加至原来集合的末尾
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
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;
}
//删除集合中指定索引之间的元素
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;
}
//检查指定的索引是否在范围内
private void rangeCheck(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
/**
* A version of rangeCheck used by add and addAll.
*/
private void rangeCheckForAdd(int index) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
//
private String outOfBoundsMsg(int index) {
return "Index: "+index+", Size: "+size;
}
//从集合中删除指定集合中所包含的所有元素
public boolean removeAll(Collection<?> c) {
Objects.requireNonNull(c);
return batchRemove(c, false);
}
//只保留指定1集合中锁包含的元素
public boolean retainAll(Collection<?> c) {
Objects.requireNonNull(c);
return batchRemove(c, true);
}
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;
}
// java.io.Serializable的写入函数
// 将ArrayList的“容量,所有的元素值”都写入到输出流中
private void writeObject(java.io.ObjectOutputStream s)
throws java.io.IOException{
// Write out element count, and any hidden stuff
int expectedModCount = modCount;
s.defaultWriteObject();
//写入“数组的容量”
s.writeInt(size);
//写入“数组中的每一个元素”
for (int i=0; i<size; i++) {
s.writeObject(elementData[i]);
}
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}
// java.io.Serializable的读取函数:根据写入方式读出
// 先将ArrayList的“容量”读出,然后将“所有的元素值”读出
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
elementData = EMPTY_ELEMENTDATA;
// Read in size, and any hidden stuff
s.defaultReadObject();
//从输入流中读取ArrayList的“容量”
s.readInt(); // ignored
if (size > 0) {
// be like clone(), allocate array based upon size not capacity
int capacity = calculateCapacity(elementData, size);
SharedSecrets.getJavaOISAccess().checkArray(s, Object[].class, capacity);
ensureCapacityInternal(size);
Object[] a = elementData;
//从输入流中将“所有的元素值”读出
for (int i=0; i<size; i++) {
a[i] = s.readObject();
}
}
}
//返回集合自身的迭代器。从集合中的指定位置开始。
public ListIterator<E> listIterator(int index) {
if (index < 0 || index > size)
throw new IndexOutOfBoundsException("Index: "+index);
return new ListItr(index);
}
public ListIterator<E> listIterator() {
return new ListItr(0);
}
//以指定的顺序返回集合元素
public Iterator<E> iterator() {
return new Itr();
}
private class Itr implements Iterator<E> {
int cursor; // index of next element to return
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount;
Itr() {}
}
- ArrayList的扩容机制
此扩容机制应该是对ArrayList效率影响的较大的因素,因为每当执行Add、AddRange、Insert、InsertRange等元素添加操作时,每次都会检查集合内部容量
是否足够,每当添加一个元素都会以当前容量的1.5倍来重新构建一个新的数组,将旧的集合中的元素拷贝至新的集合中,然后删除旧的集合。这个临界点的扩容
机制相对来说是比较影响效率的。
- TrimSize方法
此方法用于将ArrayList固定到实际元素的大小,当动态数组元素确定不再进行添加元素操作时,可以调用此方法来释放空余的内存。
- 内部Object类型的影响
对于一般的·引用类型而言,影响不是很大。但是对于值数据来说,向ArrayList中添加修改元素都会引起拆箱和装箱的操作,频繁的操作可能会影响效率。
(但多数的应用都是使用值类型数据,所以要使用就要承担一部分的效率损失,但是不会很大。)
- 效率损失
若我们频繁的使用indexOf、Contains等方法,会引起效率损失。
首先我们应该明确,ArrayList是动态数组,不支持通过key和value来进行快速的访问,实则我们调用的IndexOf、Contains方法都是执行简单的循环来查找
元素,索引效率较慢,。若有此要求可以使用Hashtable和SortedList等键值对结合。
- ArrayList的优缺点
ArrayList实现了List接口,ArrayList的底层数据结构是数组。
数组结构的优点是:**方便对集合进行快速的随机访问** 若要经常根据索引位置访问集合对象使用ArrayList较好。
数组结构的缺点是:**向指定索引位置插入和删除速度较慢**若要经常性地插入和删除指定索引的对象,则效率较低,因为当插入或删除时,会同时将索引
后的元素相应的向后或向前移动。如果在指定索引后有大量元素,则会严重影响效率。
LinkedList
LinkedList是继承自AbstractSequentialList的双向链表;可以被当做堆栈、队列、双端队列来进行处理。
LinkedList实现List接口,可以对其进行队列操作。
LinkedList实现Deque接口(JDK1.6),Deque支持在两端进行操作,即可以当作双端队列来使用。
LinkedList实现了Cloneable接口,即覆盖了clone函数,可以对其进行克隆。
LinkedList实现了java.io.Serializable接口,表明了LinkedList支持序列化,可通过序列化来进行传输
LinkedList是非同步的。
- LinkedList的构造器
//构造一个空列表(默认构造函数)
public LinkedList() {
}
//创建一个LinkedList,包含Collection中的所有元素
public LinkedList(Collection<? extends E> c) {
this();
addAll(c);
}
- AbstractSequentialList简介(LinkedList的父类)
AbstractSequentialList是LinkedList的父类,实现了get、set、add、remove等方法,这些方法都是随机访问List的;LinkedList是继承自其的双向链表,
同样也可以是使用上述方法来进行随机访问。
- LinkedList类的继承结构
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable
LinkedList本质是双向链表。继承自AbstractSequentialList接口,并且实现了Deque接口。
LinkedList有三个重要的成员变量:size(是双向链表中节点的个数),first(指向第一个节点的指针),last(指向最后一个节点的指针)
- LinkedList部分源码剖析
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable
{
//LinkedList中元素的个数
transient int size = 0;
//指向第一个节点的指针
transient Node<E> first;
//指向最后一个节点的指针
transient Node<E> last;
//此处省去构造函数
//创建新节点作为第一个元素
private void linkFirst(E e) {
final Node<E> f = first;
//创建新的节点
final Node<E> newNode = new Node<>(null, e, f);
first = newNode;
if (f == null)
last = newNode;
else
f.prev = newNode;
//链表长度+1
size++;
modCount++;
}
//创建新节点作为尾节点
void linkLast(E e) {
final Node<E> l = last;
final Node<E> newNode = new Node<>(l, e, null);
last = newNode;
if (l == null)
first = newNode;
else
l.next = newNode;
size++;
modCount++;
}
//返回链表中第一个元素()
public E getFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
//做最初已经将新的元素作为表头first所以第一个元素就是first所包含的元素
return f.item;
}
//返回链表中最后一个元素
public E getLast() {
final Node<E> l = last;
if (l == null)
throw new NoSuchElementException();
//直接返回尾节点所含元素
return l.item;
}
//清空双向链表
public void clear() {
//从表头开始向后遍历,对每个节点执行以下操作
//先对当前节点进行保存
//设置其内容为null
//设置节点的下一个节点和前一个节点为null
//设置后一个节点为“当前节点”
for (Node<E> x = first; x != null; ) {
Node<E> next = x.next;
x.item = null;
x.next = null;
x.prev = null;
x = next;
}
first = last = null;
//并设置其大小为0
size = 0;
modCount++;
}
//返回指定位置对应节点的值
public E get(int index) {
checkElementIndex(index);
return node(index).item;
}
//设置指定的节点对应值为指定值
public E set(int index, E element) {
checkElementIndex(index);
Node<E> x = node(index);
E oldVal = x.item;
x.item = element;
return oldVal;
}
//在index之前插入节点的值为element
public void add(int index, E element) {
checkPositionIndex(index);
if (index == size)
linkLast(element);
else
//进行在指定节点前插入
linkBefore(element, node(index));
}
//删除指定位置的节点
public E remove(int index) {
checkElementIndex(index);
return unlink(node(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;
}
}
//查找指定值所对应的索引,从头开始向尾部进行查找,若找不到返回-1
public int indexOf(Object o) {
int index = 0;
if (o == null) {
for (Node<E> x = first; x != null; x = x.next) {
if (x.item == null)
return index;
index++;
}
} else {
for (Node<E> x = first; x != null; x = x.next) {
if (o.equals(x.item))
return index;
index++;
}
}
return -1;
}
//查找指定值所对应的索引,从尾部开始向头部进行查找,若找不到返回-1
public int lastIndexOf(Object o) {
int index = size;
if (o == null) {
for (Node<E> x = last; x != null; x = x.prev) {
index--;
if (x.item == null)
return index;
}
} else {
for (Node<E> x = last; x != null; x = x.prev) {
index--;
if (o.equals(x.item))
return index;
}
}
return -1;
}
//返回第一个节点(如第一个节点为null,则返回null)
public E peekFirst() {
final Node<E> f = first;
return (f == null) ? null : f.item;
}
//返回最后一个节点(如最后一个节点为null,则返回null)
public E peekLast() {
final Node<E> l = last;
return (l == null) ? null : l.item;
}
//返回“指定位置到末尾的全部节点”对应的ListIterator对象(List迭代器)
public ListIterator<E> listIterator(int index) {
checkPositionIndex(index);
return new ListItr(index);
}
//List迭代器
private class ListItr implements ListIterator<E> {
//上一个返回的节点
private Node<E> lastReturned;
//下一个节点
private Node<E> next;
//下一个节点对应的索引值
private int nextIndex;
//期望的改变计数器,用来实现fail-fast机制
private int expectedModCount = modCount;
//构造函数(从index开始进行迭代)
ListItr(int index) {
next = (index == size) ? null : node(index);
nextIndex = index;
}
//是否存在下一个元素
public boolean hasNext() {
//通过元素的索引是否小于“双向链表的长度”来判断是否到达最后位置
return nextIndex < size;
}
//获取下一个元素
public E next() {
checkForComodification();
if (!hasNext())
throw new NoSuchElementException();
lastReturned = next;
//next指向链表的下一个元素
next = next.next;
nextIndex++;
return lastReturned.item;
}
//是否存在上一个元素
public boolean hasPrevious() {
return nextIndex > 0;
}
//获取上一个元素
public E previous() {
checkForComodification();
if (!hasPrevious())
throw new NoSuchElementException();
//next指向链表的上一个元素
lastReturned = next = (next == null) ? last : next.prev;
nextIndex--;
return lastReturned.item;
}
//获取下一个元素的索引
public int nextIndex() {
return nextIndex;
}
//获取上一个元素的索引
public int previousIndex() {
return nextIndex - 1;
}
//删除当前元素(删除双向链表中的当前节点)
public void remove() {
checkForComodification();
if (lastReturned == null)
throw new IllegalStateException();
Node<E> lastNext = lastReturned.next;
unlink(lastReturned);
if (next == lastReturned)
next = lastNext;
else
nextIndex--;
lastReturned = null;
expectedModCount++;
}
//设置当前节点为指定值
public void set(E e) {
if (lastReturned == null)
throw new IllegalStateException();
checkForComodification();
lastReturned.item = e;
}
//将指定元素添加至当前节点的前面
public void add(E e) {
checkForComodification();
lastReturned = null;
if (next == null)
linkLast(e);
else
linkBefore(e, next);
nextIndex++;
expectedModCount++;
}
public void forEachRemaining(Consumer<? super E> action) {
Objects.requireNonNull(action);
while (modCount == expectedModCount && nextIndex < size) {
action.accept(next.item);
lastReturned = next;
next = next.next;
nextIndex++;
}
checkForComodification();
}
//判断modCount和expectedModCount是否相等,来
实现fail-fast机制
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
}
- LinkedList总结
(1)LinkedList是通过双向链表实现的,包含一个重要的内部类Node。Node是双向链表节点所对应的数结构,包含当前节点
的值,上一个节点,下一个节点。
(2)LinkedList不存在容量不足的问题。
(3)LinkedList的克隆函数是将全部元素克隆到一个新的LinkedList对象中
(4)LinkedList实现了Deque接口,在此接口中定义了双端队列两端访问元素的方式。
- LinkedList作为FIFO(先进先出)队列
等效方法:
队列方法 LinkedList方法
add()<------->addLast()
offer()<------->offLast()
remove()<------->removeFirst()
poll()<------->pollFirst()
element()<------->getFirst()
peek()<------->peekFirst()
- LinkedList作为LIFO(先进后出)的栈
栈方法 LinkedList方法
push()<------->addFirst()
pop()<------->removeFirst()
peek()<------->peekFirst()
- LinkedList集合的遍历方式
(1)通过迭代器遍历,即使用Iterator来进行遍历
for(Iterator iterator = list.iterator;iterator.hasNext();){
iterator.next();
}
(2)通过随机访问遍历LinkedList
int size = list.size();
for(int i = 0; i < size;i++){
list.get(i);
}
(3)通过pollFirst()遍历LinkedList
while(list.pollFirst()!=null);
(4)通过pollLast()遍历LinkedList
while(list.pollLast()!=null);
(5)通过removeFirst()遍历LinkedList
while(list.removeFirst()!=null);
(6)通过removeLast()遍历LinkedList
while(list.removeLast()!=null);
Vector
(1)Vector是动态数组实现的List,和ArrayList一样,存在自动扩容机制。
(2)Vector是在JDK1.0引入的,因为许多方法都加了同步语句,所以是线程安全的。
(3)Vector适用于进行快速查找和修改的场景,不适合用于随机的查找和删除操作。
(4)Vector初始化容量为10,扩容由初始化容量和capacityIncrement共同决定。
(5)Vector允许元素为null
(6)Vector不推荐使用,若不需要考虑线程安全的实现,则可用ArrayList来代替Vector。
- Vector的继承结构
- Vector构造器
Vector中提供四个构造函数
//两个整形参数的构造方法,
//initialCapacity为初始容量大小,
//capacityIncrement为扩容增加值,大于0则增加capacityIncrement,否则,翻倍
public Vector(int initialCapacity, int capacityIncrement) {
super();
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
this.elementData = new Object[initialCapacity];
this.capacityIncrement = capacityIncrement;
}
//一个参数构造方法
//initialCapacity默认容量大小。当增加数据导致容量增加时,每次容量会增加一倍
public Vector(int initialCapacity) {
this(initialCapacity, 0);
}
//默认构造方法
public Vector() {
this(10);
}
//创建一个包含collection的Vector
public Vector(Collection<? extends E> c) {
elementData = c.toArray();
elementCount = elementData.length;
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, elementCount, Object[].class);
}
- Vectord的扩容
Vector在每次增加元素时,都要确保有足够的容量。当容量不足以容纳当前元素时,就先
看构造方法中传入的容量增长量参数CapacityIncrement是否为0.
(1)如果不为0,就设置新的容量为旧的容量加上容量增长量
(2)如果为0,就设置容量为旧容量的2倍
(3)如果设置后新容量还是不够,则直接新容量设置为传入的参数(即所需要的容量)
(4)而后同样用Arrays.copyof()方法将元素拷贝到新的数组
public synchronized void ensureCapacity(int minCapacity) {
if (minCapacity > 0) {
modCount++;
ensureCapacityHelper(minCapacity);
}
}
private void ensureCapacityHelper(int minCapacity) {
// 需要进行扩容
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
private void grow(int minCapacity) {
// 旧容量 = 所含元素的长度
int oldCapacity = elementData.length;
//扩容大小 ? capacityIncrement 或 翻倍
int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
capacityIncrement : oldCapacity);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
elementData = Arrays.copyOf(elementData, newCapacity);
}
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
Vector的扩容大小由当前元素个数oldCapacity = elementData.length和属性capacityIncrement共同决定:
(1)capacityIncrement<=0,则扩容大小为oldCapacity,即翻倍
(2)若capacityIncrement>0,则扩容大小为capacityIncrement的大小。
(3)Vector容量大小限制:
在JDK1.6之前扩容没有限制容量大小,但是在JDK1.8中限制了容量大小最大为Integer.MAX_VALU(2^31 - 1).
- Vector部分源码剖析
public class Vector<E>
extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
//保存Vector数据的数组
protected Object[] elementData;
//实际的数量
protected int elementCount;
//容量增长系数
protected int capacityIncrement;
//Vector的序列版本号
private static final long serialVersionUID = -2767605614048989439L;
//构造函数(默认容量为10)
public Vector() {
this(10);
}
//指定Vector容量大小的构造函数
public Vector(int initialCapacity) {
this(initialCapacity, 0);
}
//指定Vector容量大小和“增长系数”的构造函数
public Vector(int initialCapacity, int capacityIncrement) {
super();
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
// 新建一个数组,数组容量是initialCapacity
this.elementData = new Object[initialCapacity];
// 设置容量增长系数
this.capacityIncrement = capacityIncrement;
}
//指定集合的Vector构造函数
public Vector(Collection<? extends E> c) {
// 获取“集合(c)”的数组,并将其赋值给elementData
elementData = c.toArray();
// 设置数组长度
elementCount = elementData.length;
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, elementCount, Object[].class);
}
// 将数组Vector的全部元素都拷贝到数组anArray中
public synchronized void copyInto(Object[] anArray) {
System.arraycopy(elementData, 0, anArray, 0, elementCount);
}
//将当前容量设为实际元素的个数
public synchronized void trimToSize() {
modCount++;
int oldCapacity = elementData.length;
if (elementCount < oldCapacity) {
elementData = Arrays.copyOf(elementData, elementCount);
}
}
//确定Vector的容量
public synchronized void ensureCapacity(int minCapacity) {
if (minCapacity > 0) {
// 将Vector的改变统计数+1
modCount++;
ensureCapacityHelper(minCapacity);
}
}
// 确认“Vector容量”的帮助函数
private void ensureCapacityHelper(int minCapacity) {
// overflow-conscious code
if (minCapacity - elementData.length > 0)
//进行扩容
grow(minCapacity);
}
//实际扩容函数
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
/ 当Vector的容量不足以容纳当前的全部元素,增加容量大小。
// 若 容量增量系数>0(即capacityIncrement>0),则将容量增大当capacityIncrement
// 否则,将容量增大一倍。
int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
capacityIncrement : oldCapacity);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
elementData = Arrays.copyOf(elementData, newCapacity);
}
//设置新的容量大小为newSize
public synchronized void setSize(int newSize) {
modCount++;
if (newSize > elementCount) {
// 若 "newSize 大于 Vector容量",则调整Vector的大小
ensureCapacityHelper(newSize);
} else {
// 若 "newSize 小于/等于 Vector容量",则将newSize位置开始的元素都设置为null
for (int i = newSize ; i < elementCount ; i++) {
elementData[i] = null;
}
}
elementCount = newSize;
}
//返回Vector的总容量
public synchronized int capacity() {
return elementData.length;
}
// 返回“Vector的实际大小”,即Vector中元素个数
public synchronized int size() {
return elementCount;
}
// 判断Vector是否为空
public synchronized boolean isEmpty() {
return elementCount == 0;
}
// 返回“Vector中全部元素对应的Enumeration”
public Enumeration<E> elements() {
// 通过匿名类实现Enumeration
return new Enumeration<E>() {
int count = 0;
// 是否存在下一个元素
public boolean hasMoreElements() {
return count < elementCount;
}
// 获取下一个元素
public E nextElement() {
synchronized (Vector.this) {
if (count < elementCount) {
return elementData(count++);
}
}
throw new NoSuchElementException("Vector Enumeration");
}
};
}
// 从index位置开始向后查找元素(o)。
// 若找到,则返回元素的索引值;否则,返回-1
public synchronized int indexOf(Object o, int index) {
if (o == null) {
// 若查找元素为null,则正向找出null元素,并返回它对应的序号
for (int i = index ; i < elementCount ; i++)
if (elementData[i]==null)
return i;
} else {
// 若查找元素不为null,则正向找出该元素,并返回它对应的序号
for (int i = index ; i < elementCount ; i++)
if (o.equals(elementData[i]))
return i;
}
return -1;
}
// 从后向前查找元素(o)。开始位置是从前向后的第index个数;
// 若找到,则返回元素的“索引值”;否则,返回-1。
public synchronized int lastIndexOf(Object o, int index) {
if (index >= elementCount)
throw new IndexOutOfBoundsException(index + " >= "+ elementCount);
if (o == null) {
// 若查找元素为null,则反向找出该元素,并返回它对应的序号
for (int i = index; i >= 0; i--)
if (elementData[i]==null)
return i;
} else {
// 若查找元素不为null,则反向找出该元素,并返回它对应的序号
for (int i = index; i >= 0; i--)
if (o.equals(elementData[i]))
return i;
}
return -1;
}
//返回Vector中第index个元素,若index越界则抛异常
public synchronized E elementAt(int index) {
if (index >= elementCount) {
throw new ArrayIndexOutOfBoundsException(index + " >= " + elementCount);
}
return elementData(index);
}
//返回Vector中第一个元素,失败抛异常
public synchronized E firstElement() {
if (elementCount == 0) {
throw new NoSuchElementException();
}
return elementData(0);
}
//返回Vector中最后一个元素,失败抛异常
public synchronized E lastElement() {
if (elementCount == 0) {
throw new NoSuchElementException();
}
return elementData(elementCount - 1);
}
… …
… …
- Vector的遍历方式
通过Iterator进行遍历
for(Iterator iter = vec.iterator(); iter.hasNext();)
iter.next();
通过索引值遍历
Integer value = null;
int size = vec.size();
for (int i=0; i<size; i++) {
value = (Integer)vec.get(i);
}
Enumeration遍历
Integer value = null;
Enumeration enum = vector.elements();
while (enum.hasMoreElements()) {
value = (Integer)enum.nextElement();
}
- Fail-Fast机制
Vector采用了快速失败机制,通过记录modCount参数来实现。在面对并发的修改时,迭代器很快就会完全失败,
而不是冒着在某一个不确定的时间发生任意不确定行为的风险。
Fail-Fast是Java集合的一种错误检测机制。当多个线程对集合进行结构上的改变操作时,有可能会产生Fail-Fast机制
但是也只是可能,而不是一定。例如:假设存在两个线程(线程1、线程2),线程1通过Iterator在遍历集合A中的元素,
在某个时候线程2修改了集合A的结构(是结构上面的修改,而不是简单的修改集合元素的内容),那么这个时候程序就会抛出
ConcurrentModificationException 异常,从而产生fail-fast机制。
ArrayList和Vector的比较
ArrayList和Vector最大的区别就是Vector是线程安全的,而ArrayList是线程不安全的,此外,还有以下区别。
- ArrayList不可以设置扩展的容量,默认是1.5倍
- Vector可以设置扩展的容量,如果没有设置,默认2倍
- ArrayList无参构造方法中初始容量为0(第一次调用add()方法会更新到10)
- Vector的无参构造方法中初始容量是10
- ArrayList是线程不安全的
- Vector是线程安全的
Collections.syschronizedList和Vector的比较
- Vector是JDK1.0开始使用的,而集合框架爱是从JDK1.2开始加入的。
- SyschronzedList和Vector的最主要区别
(1)SyschronzedList有很好的扩展性和兼容功能,它可以将所有的List子类转化为线程安全的类
(2)使用SyschronzedList时,若要进行遍历则需要手动进行同步处理
(3)SyschronzedList可以指定锁定的对象