List集合是线性数据结构的主要表现,集合元素通常存在着明确的上一个和下一个元素,也存在着明确的第一个元素和最后一个元素。最常用的类有ArrayList,LinkedList
AbstractList 抽象类
AbstractList<E>抽象类实现了List<E>接口,实现了一些基本的方法,并且在add,remove,set方法中直接抛出了UnsupportedOperationException异常,这就意味着如果子类直接继承这个AbstractList然后不重写就直接使用这写方法就会抛出UnsupportedOperationException异常。
ArrayList
需实现set方法 ---- AbstractLIst
根据索引访问集合元素 ----List
可以随机访问 ----RandomAccess
能克隆---cloneable标记接口,重写Object 中的clone 方法(否则抛出CloneNotSupportedException 克隆不被支持)
可以被序列化或者反序列化----Serializable
非同步---若想线程安全:Collections.synchronizedList(new LinkedList());
ArrayList 底层是数组(实现接口RandomAccess 随机访问标识接口),擅长随机方法,初始容量为10,每次扩容为1.5倍,线程不安全,若想线程安全:Collections.synchronizedList(new ArrayList ());
ArrayList构造方法
/**
*默认构造函数,使用初始容量10构造一个空列表(无参数构造)
*/
public ArrayListDemo(){
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
/**
* 带初始容量参数的构造函数。(用户自己指定容量)
*/
public ArrayListDemo(int initCapacity){
if(initCapacity>0){
this.elementData = new Object[initCapacity];
}
else if(initCapacity ==0){
this.elementData = EMPTY_ELEMENTDATA;
}
else {
throw new IllegalArgumentException("illegal initCapacity:" + initCapacity);
}
}
/**
*构造包含指定collection元素的列表,这些元素利用该集合的迭代器按顺序返回
*如果指定的集合为null,throws NullPointerException。
*/
public ArrayListDemo(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;
}
if( 0 != (size = elementData.length)){
if (Object[].class != elementData.getClass()){
elementData = Arrays.copyOf(elementData,size, Object[].class);
}
}else{
this.elementData = EMPTY_ELEMENTDATA;
ArrayList扩容机制
自己模仿实现的扩容机制:
public boolean add(Object o) {
ensureCapacityInternalDemo(size + 1); // Increments modCount!!
//这里看到ArrayList添加元素的实质就相当于为数组赋值
elementData[size] = o;
size = size +1;
return true;
}
public void ensureCapacityInternalDemo(int minCapacity){
if(DEFAULTCAPACITY_EMPTY_ELEMENTDATA == this.elementData){
minCapacity = Math.max(minCapacity, DEFALUT_CAPACITY);
}
ensureExplicitCapacityDemo(minCapacity);
}
public void ensureExplicitCapacityDemo(int minCapacity){
if(minCapacity - this.elementData.length > 0){
growDemo(minCapacity);
}
}
public void growDemo(int minCapacity){
//旧的容量
int oldCapacity = this.elementData.length;
//计算新的容量:将oldCapacity右移一位,也就是oldCapacity的一半
int newCapacity = oldCapacity + (oldCapacity >>1);
//判断新的容量是否满足需求:是否小于当前要求的最小容量,若是有小于则新的容量为最小容量
if(minCapacity - newCapacity>0){
newCapacity = minCapacity;
}
//判断新的容量是否大于最大值:MAX_ARRAY_SIZE,若是大于为Integer.MAX_VALUE
if(newCapacity - MAX_ARRAY_SIZE>0){
newCapacity = Integer.MAX_VALUE;
}
//将源素组复制到新数组
elementData = Arrays.copyOf(elementData,newCapac
若是新建没有给定初始容量,如List list = new ArrayListDemo(),此时的list 的为空,只有在第一次add 操作的时候,会进行首次扩容,大小为10,若下图所示,newCapacity/minCapacity 即为list 内部的数组elementData.length,因此为了性能考虑,在新建的时候最好可以给定list 的初始容量,减少反复扩容的性能消耗
若是新建给定初始容量,如List list = new ArrayListDemo(10),那么在前10次add操作的时候都不会扩容,因为当add前10个元素的时, elemenetData.length为10,因为执行了ensureExplicitCapacityDemo方法, minCapacity - this.elementData.length > 0 不成立,所以不会进入growDemo 方法,不会进行扩容,但是当第十一次add 操作时,minCapacity 为11, minCapacity - this.elementData.length > 0 成立,会进入 growDemo 方法,得到新的容量为为旧容量的1.5倍,即为15,最后list.size 为11,运行结果看下图
LinkedList
LinkedList底层是链表,同时实现List<E>, Deque<E>, Cloneable, Serializable
是个接口,继承了AbstractSequentialList抽象类,因此LinkedList出游如下特性:
需实现set方法 ---- AbstractLIst
根据索引访问集合元素 ----List
双向队列,既可以是栈,也可以是队列 ----Deque
能克隆---cloneable标记接口,重写Object 中的clone 方法(否则抛出CloneNotSupportedException 克隆不被支持)
可以被序列化或者反序列化----Serializable
非同步---若想线程安全:Collections.synchronizedList(new LinkedList());
所以LinkedList 不支持随机访问,但是插入删除性能好,没有初始大小,也没有扩容机制,作为双向链表,只有在头部或者尾部新增即可,
内部分析结构图
内部类
//内部类
private static class Node<E>{
E data; //节点值
Node pre;// 前驱节点
Node next;//后继节点
Node( Node pre,E data, Node next){
this.data= data;
this.pre =pre;
this.next = next;
}
}
LinkedList 构造方法
//空构造方法
public LinkedListDemo(){};
//已有集合的构造方法
public LinkedListDemo(Collection <? extends E> collection){
this();
addAll(collection);
}
模拟LinkedList 源码实现的重要方法
package com.list;
import java.util.Collection;
import java.util.Iterator;
import java.util.NoSuchElementException;
public class LinkedListDemo<E> {
//内部类
private static class Node<E>{
E data; //节点值
Node pre;// 前驱节点
Node next;//后继节点
Node( Node pre,E data, Node next){
this.data= data;
this.pre =pre;
this.next = next;
}
}
transient int size;
transient Node<E> fisrt;
transient Node<E> last;
//空构造方法
public LinkedListDemo(){};
//已有集合的构造方法
public LinkedListDemo(Collection <? extends E> collection){
this();
addAll(collection);
}
public int size() {
return this.size;
}
//链接在尾部
private boolean linkLast(E e){
final Node<E> l = last;
final Node<E> newNode = new Node<E>(l, e, null);
last = newNode;
if(null == l){
fisrt = last;
}
else{
l.next = newNode;
}
size ++;
return true;
}
//增加在头部
public boolean LinkFirst(E e){
final Node<E> f = fisrt;
final Node<E> newNode = new Node<E>(null,e,f);
fisrt = newNode;
if(null == f){
last = fisrt;
}else{
f.pre = fisrt;
}
size++;
return true;
}
//默认添加在尾部
public boolean add(E data){
this.linkLast(data);
return true;
}
//加入到指定位置
public boolean add(E e, int index){
//判断Index 是否在 0-size 之间
checkPositionCheck(index);
if(index == size){
this.linkLast(e);
}else{
//获取index 索引的节点
Node tempNode = this.Node(index);
//将数据插入的该节点之前
this.linkedBefore(e,tempNode);
}
return true;
}
private Node Node(int index){
Node result;
if(index < size){
result = this.fisrt;
for(int i=0;i<index;i++){
result = result.next;
}
}else{
result = this.last;
}
return result;
}
//将data 插入到 node 节点之前
private void linkedBefore(E data, Node<E> node){
Node tempPre = node.pre;
Node newNode = new Node(tempPre,data, node);
node.pre = newNode;
if(null == tempPre){
this.fisrt = newNode;
}else{
tempPre.next = newNode;
}
++this.size;
}
public boolean addAll(Collection collection){
addAll(size,collection);
return true;
}
public boolean addAll(int index, Collection<? extends E> collection){
//1.检查index 是否为有效索引
checkPositionCheck(index);
//2.将集合中的元素转存到数组中
Object[] a = collection.toArray();
int numNew = a.length;
if (0 == numNew) return false;
//3.获取要插入位置的节点
Node tempNode = Node(index);
//4.获取插入节点的前驱
Node tempNodePre = tempNode.pre;
//数据遍历插入
for(Object o : a){
//创新新的节点
Node newTemp = new Node(tempNodePre,o,null);
if(null == tempNodePre){
fisrt = newTemp;
}else{
tempNodePre.next = newTemp;
}
tempNodePre = newTemp;
}
if(null == tempNode){
last = tempNodePre;
}else{
tempNodePre.next = tempNode;
tempNode.pre = tempNodePre;
}
size = size + numNew;
return true;
}
//根据指定索引返回数据
public E get(int index){
//1.检查索引是否有效
checkPositionCheck(index);
//2.获取index出的节点
Node<E> node = Node(index);
return node.data;
}
//获取头节点(index=0)数据方法
public E getFirst(){
final Node<E> f =fisrt;
if (null == f )
throw new NoSuchElementException();
return f.data;
}
public E element(){ return getFirst();}
public E peek(){
final Node<E> f = fisrt;
return (null == f)?null:f.data;
}
public E peekFirst() {
final Node<E> f = fisrt;
return (f == null) ? null : f.data;
}
//获取尾节点(index=-1)数据方法:
public E getLast() {
final Node<E> l = last;
if (l == null)
throw new NoSuchElementException();
return l.data;
}
public E peekLast() {
final Node<E> l = last;
return (l == null) ? null : l.data;
}
//根据对象得到索引的方法:int indexOf(Object o): 从头遍历找
public int indexOf(Object o){
int index =0;
if(null == o){
for(Node x = fisrt;x!=null;x = x.next){
if(null == x.data){
return index;
}
index ++;
}
}else{
for(Node x = fisrt;x!=null;x = x.next){
if(o.equals(x.data)){
return index;
}
index ++;
}
}
return -1;
}
//int lastIndexOf(Object o): 从尾遍历找
public int lastIndexOf(Object o){
int index =size -1 ;
if(null == o){
for(Node x = last;x!=null;x = x.pre){
if(null == x.data){
return index;
}
index --;
}
}else{
for(Node x = last;x!=null;x = x.pre){
if(o.equals(x.data)){
return index;
}
index ++;
}
}
return -1;
}
//检查对象o是否存在于链表中
public boolean contains(Object o){
return indexOf(o) !=-1;
}
//remove(Object o): 删除指定元素
public boolean remove(Object o){
int index =0;
if(null == o){
for(Node x = fisrt;x!=null;x = x.next){
if(null == x.data){
unLinked(x);
return true;
}
}
}else{
for(Node x = fisrt;x!=null;x = x.next){
if(o.equals(x.data)){
unLinked(x);
return true;
}
}
}
return false;
}
//删除指定位置的元素
public E remove(int index){
//检查索引
checkPositionCheck(index);
//获取该索引节点
Node<E> nodeTemp = Node(index);
//删除节点nodeTemp
return unLinked(nodeTemp);
}
public E pop() {
return removeFirst();
}
public E remove() {
return removeFirst();
}
public E removeFirst() {
final Node<E> f = fisrt;
if (f == null)
throw new NoSuchElementException();
return unlinkFirst(f);
}
public E removeLast() {
final Node<E> l = last;
if (l == null)
throw new NoSuchElementException();
return unlinkLast(l);
}
public E pollLast() {
final Node<E> l = last;
return (l == null) ? null : unlinkLast(l);
}
public E unlinkFirst(Node<E> node){
E element = node.data;
fisrt = node.next;
node.data = null;
node.next = null;
if(null == fisrt){
last = null;
}else{
fisrt.pre = null;
}
size--;
return element;
}
public E unlinkLast(Node<E> node){
E element = node.data;
last = node.pre;
node.data = null;
node.next = null;
if(null == last){
fisrt = null;
}else{
last.next = null;
}
size--;
return element;
}
//删除节点node:节点包含三部分,前驱指针,元素,后继指针,分别删除即可
private E unLinked(Node<E> node){
final E element = node.data;
//前驱
Node<E> tempPre = node.pre;
//后继
Node<E> tempNext = node.next;
//前驱指针删除
if (null == tempNext) {
fisrt = tempNext;
}else{
tempPre.next = tempNext;
node.pre =null;
}
//后继指针删除
if(null == tempNext){
last = tempPre;
}else{
tempNext.pre = tempPre;
node.pre = null;
}
//删除元素
node.data = null;
//修改列表的size
size --;
return element;
}
//校验索引是否有效
private void checkPositionCheck(int index){
if(!this.isPositionIndex(index)){
throw new IndexOutOfBoundsException("size is:" +size +"; index is:" + index);
}
}
//检查是index 是否有效索引
private boolean isPositionIndex(int index){
return index>=0 && index<=this.size;
}
public void clear() {
Node<E> node = null;
for(Node<E> x= fisrt; x!=null; x = x.next){
node = x.next;
x.pre =null;
x.data = null;
x.next = null;
}
this.fisrt = this.last =null;
this.size =0;
}
public Iterator iterator() {
return new Iterator() {
// 定义一个变量指向当前迭代的节点,初始值是表头
Node current = fisrt;
@Override
public Object next() {
// 获得当前节点的内容
Object o = current.pre;
// 使指针指向下一个节点
current = current.next;
return o;
}
@Override
public boolean hasNext() {
// 判断当前节点是否为空,部位空的话表面链表还有未迭代的节点,返回true
ArrayLsit VS LinkeList
1、底层实现--ArrayList:数组;LinkedList:链表;
2、随机访问--ArrayList支撑,LinkedList不支持;
3、插入删除--add/remove(index):ArrayList时间复制度为O(n-index),LinkedList接近n(1);
4、内存浪费--ArrayList提现在有一定预留空间,LinkedList提现需要存储前驱后继指针;
5、都是非同步的。
Vector
与ArrayList相似,但是Vector是同步的。所以说Vector是线程安全的动态数组。它的操作与ArrayList几乎一样。
源码分析
protected Object[] elementData;//存放元素的数组 也就是vector底层是数组
protected int elementCount;//记录存放的元素个数
protected int capacityIncrement;//增长因子 和增长容量相关 下面会介绍
private static final long serialVersionUID = -2767605614048989439L;//序列版本号
public Vector(int initialCapacity, int capacityIncrement) {
super();
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
this.elementData = new Object[initialCapacity];
this.capacityIncrement = capacityIncrement;
}
public Vector(int initialCapacity) {
this(initialCapacity, 0);
}
public Vector() {
this(10);
}
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);
}
Vector 的初始容量为10,加载因子默认为0
添加扩容机制
add方法:
public synchronized void addElement(E obj) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = obj;
}
可以看到实现方式为modCount加1,表示修改次数加了一次,这个字段的作用体现在了快速失败机制中,而所谓快速失败机制就是如果在迭代的过程中,这个Vector被修改了,就不冒险继续迭代,而是直接抛出ConcurrentModificationException异常结束。然后调用了一个ensureCapacityHelper(elementCount+1)以确保容量足够,最后将obj加入到存放元素的数组。整个方法是用synchronized关键字修饰的,所以是线程安全的。
ensureCapacityHelper():
private void ensureCapacityHelper(int minCapacity) {
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
如果所需最小容量比当前存放的数组容量还要大,就调用grow()方法进行扩容,由于执行这个方法的时候已经是在synchronized方法内,所以这个ensureCapacityHelper()方法没有用synchronized关键字修饰。
grow()方法:
private void grow(int minCapacity) {
int oldCapacity = elementData.length;
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);
}
}
Vector,如果capacityIncrement大于0,newCapacity为capacityIncrement+oldCapacity,否则直接等于oldCapacity的两倍。如果容量仍然不够,newCapacity直接等于所需最小容量。如果newCapacity这个时候比MAX_ARRAY_SIZE还大,也就是比Integer.MAX_VALUE-8还要大就通过hugeCapacity(minCapacity)方法返回一个合适的容量,作为newCapacity,最后使用Arrays.copyOf(elementData,newCapacity)方法来实现数组的扩容。
hugeCapacity()方法如下:
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
删除机制
removeElement(Object obj)方法:
public synchronized boolean removeElement(Object obj) {
modCount++;
int i = indexOf(obj);
if (i >= 0) {
removeElementAt(i);
return true;
}
return false;
}
通过indexOf(obj)方法查找obj的索引i,然后调用removeElementAt(i)进行删除。我们直接看removeElementAt(int i)方法:
public synchronized void removeElementAt(int index) {
modCount++;
if (index >= elementCount) {
throw new ArrayIndexOutOfBoundsException(index + " >= " +
elementCount);
}
else if (index < 0) {
throw new ArrayIndexOutOfBoundsException(index);
}
int j = elementCount - index - 1;
if (j > 0) {
System.arraycopy(elementData, index + 1, elementData, index, j);
}
elementCount--;
elementData[elementCount] = null; /* to let gc do its work */
}
删除就没有添加那么麻烦,只是判断一下索引的合法性,然后就使用System.arraycopy()方法进行删除了,注意这里有个小细节:elementData[elementCount] = null,如果没有这行代码会导致elementData[elementCount]这个元素虽然不会再被使用了,但是由于这个元素仍然被elementData这个数组维护着,所以导致这个元素无法被GC回收,当这种情况出现的次数太多,就有可能导致OOM。
总结
1、底层是用数组实现的,不过是可变长的,默认初始容量是10,默认增长因子是0,如果想要加入新的元素而容量不足就需要进行扩容,如果增长因子大于0,就增长负载因子个数的容量,否则增长为原来容量的两倍,如果容量仍然不够,就直接增长为所需最小容量。频繁地扩容容易引起效率问题,所以最好在调用构造函数的时候指定一个合适的容量或者调用ensureCapacity()方法进行扩容到适当的容量。
2、这个类是线程安全的,采用了快速失败机制,提供了增加、删除元素,更加方便快捷。
3、线程安全不意味着对于这个容器的任何操作都是线程安全的,比如在进行迭代的时候,如果不增加一些代码保证其线程安全,其他线程是可以对这个容器做出修改的,这样也就会导致抛出ConcurrentModificationException异常
Stack
Stack继承自Vector,实现一个后进先出的堆栈。Stack提供5个额外的方法使得Vector得以被当作堆栈使用。基本的push和pop 方法,还有peek方法得到栈顶的元素,empty方法测试堆栈是否为空,search方法检测一个元素在堆栈中的位置。Stack刚创建后是空栈。
源码分析
package java.util;
public
class Stack<E> extends Vector<E> {
// 版本ID。这个用于版本升级控制,这里不须理会!
private static final long serialVersionUID = 1224463164541339165L;
// 构造函数
public Stack() {
}
// push函数:将元素存入栈顶
public E push(E item) {
// 将元素存入栈顶。
// addElement()的实现在Vector.java中
addElement(item)
return item;
}
// pop函数:返回栈顶元素,并将其从栈中删除
public synchronized E pop() {
E obj;
int len = size();
obj = peek();
// 删除栈顶元素,removeElementAt()的实现在Vector.java中
removeElementAt(len - 1);
return obj;
}
// peek函数:返回栈顶元素,不执行删除操作
public synchronized E peek() {
int len = size();
if (len == 0)
throw new EmptyStackException();
// 返回栈顶元素,elementAt()具体实现在Vector.java中
return elementAt(len - 1);
}
// 栈是否为空
public boolean empty() {
return size() == 0;
}
// 查找“元素o”在栈中的位置:由栈底向栈顶方向数
public synchronized int search(Object o) {
// 获取元素索引,elementAt()具体实现在Vector.java中
int i = lastIndexOf(o);
if (i >= 0) {
return size() - i;
}
return -1;
迭代器
list 遍历
if(list instanceof RandomAccess){
for(int i = 0;i<size;i++){
//遍历操作
}
}
else{
Iterator<?> iterator = list.iterator();
while(iterator.hasNext()){
//遍历操作
}
}
迭代器位置图
迭代器指向的位置是元素之前的位置
当使用语句Iterator it=List.Iterator()时,迭代器it指向的位置是Iterator1指向的位置,当执行语句it.next()之后,迭代器指向的位置后移到Iterator2指向的位置。
因此迭代器的实现:
new Iterator() {
// 定义一个变量指向当前迭代的节点,初始值是表头
Node current = fisrt;
@Override
public Object next() {
// 获得当前节点的内容
Object o = current.pre;
// 使指针指向下一个节点
current = current.next;
return o;
}
@Override
public boolean hasNext() {
// 判断当前节点是否为空,部位空的话表面链表还有未迭代的节点,返回true
return current != null;
}
};
Iterator VS ListIterator
Iterator是一个接口,它是集合的迭代器。集合可以通过Iterator去遍历集合中的元素。Iterator提供的API接口如下:
boolean hasNext():判断集合里是否存在下一个元素。如果有,hasNext()方法返回 true。
Object next():返回集合里下一个元素。
void remove():删除集合里上一次next方法返回的元素。
ListIterator接口继承Iterator接口,提供了专门操作List的方法。ListIterator接口在Iterator接口的基础上增加了以下几个方法:
boolean hasPrevious():判断集合里是否存在上一个元素。如果有,该方法返回 true。
Object previous():返回集合里上一个元素。
void add(Object o):在指定位置插入一个元素。