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):在指定位置插入一个元素。