1、ArrayList
平常一直使用的List内部为数组结构,我们来看看里面有什么:
//默认构造扩容大小
private static final int DEFAULT_CAPACITY = 10;
//空实例
private static final Object[] EMPTY_ELEMENTDATA = {};
//区分上面那个(扩容大小和上面不同)
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
//存数据的数组
transient Object[] elementData; // non-private to simplify nested class access
//List.size
private int size;
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);
}
}
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; //空数组,添加时扩容
}
我们可以发现使用默认构造函数的时候并没有设置默认的容量,其实是在add的时候去做的这件事
private void add(E e, Object[] elementData, int s) {
if (s == elementData.length)
elementData = grow(); //扩容
elementData[s] = e;
size = s + 1;
}
private Object[] grow() {
return grow(size + 1);
}
private Object[] grow(int minCapacity) {
return elementData = Arrays.copyOf(elementData,
newCapacity(minCapacity)); //新建数组复制原数组
}
/**
* Returns a capacity at least as large as the given minimum capacity.
* Returns the current capacity increased by 50% if that suffices.
* Will not return a capacity greater than MAX_ARRAY_SIZE unless
* the given minimum capacity is greater than MAX_ARRAY_SIZE.
* 返回至少和minCapacity一样大的容量
* 返回当前容量的150%
* 如果需求容量大于MAX_ARRAY_SIZE返回Integer.MAX_VALUE
*/
private int newCapacity(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity <= 0) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
return Math.max(DEFAULT_CAPACITY, minCapacity);
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return minCapacity;
}
return (newCapacity - MAX_ARRAY_SIZE <= 0)
? newCapacity
: hugeCapacity(minCapacity);
}
其他的好像也没什么好看的,clear方法情况的所有的值,并帮助内存回收
public void clear() {
modCount++;
final Object[] es = elementData;
for (int to = size, i = size = 0; i < to; i++)
es[i] = null; //help GC
}
这里引入一个很经典的问题,那就是List用foreach循环的时候调用了List的add或remove会发生什么
public static void main(String[] args) {
List<String> ll = new ArrayList<>();
ll.add("a");
ll.add("b");
ll.add("c");
for (String s : ll) {
ll.add(s); //ll.remove(s)
}
}
Exception in thread "main" java.util.ConcurrentModificationException
at java.base/java.util.ArrayList$Itr.checkForComodification(ArrayList.java:1043)
at java.base/java.util.ArrayList$Itr.next(ArrayList.java:997)
at com.example.demo.Test.main(Test.java:13)
我们发现发生了一个叫做ConcurrentModificationException的异常,我们跟踪下源代码:
private class Itr implements Iterator<E> {
int expectedModCount = modCount; //初始值相等
final void checkForComodification() {
if (modCount != expectedModCount) //不等于就抛出异常
throw new ConcurrentModificationException();
}
}
我们都知道List的foreach是简单的迭代器用法,编译器在编译的时候会使用原生的迭代器,而在使用foreach的时候调用List的remove方法,修改了modCount的值
//取自ArrayList迭代器
public E next() {
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
}
//取自ArrayList
private void fastRemove(Object[] es, int i) {
modCount++; //这里修改了
final int newSize;
if ((newSize = size - 1) > i)
System.arraycopy(es, i + 1, es, i, newSize - i);
es[size = newSize] = null;
}
所以为了避免这类问题,请不用这样使用,删除的时候使用迭代器遍历的方式。
还有一点时ArrayList中有一个静态类SubList,用的时候要小心,它的所有操作都是针对原来的List做改变。
private static class SubList<E> extends AbstractList<E> implements RandomAccess {
private final ArrayList<E> root;
private final SubList<E> parent;
private final int offset;
private int size;
public SubList(ArrayList<E> root, int fromIndex, int toIndex) {
this.root = root;
this.parent = null;
this.offset = fromIndex;
this.size = toIndex - fromIndex;
this.modCount = root.modCount;
}
}
2、LinkedList
LinkedList为链表结果,内部维护了一个双向链表,每次插入时从链表尾巴插入数据
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 boolean add(E e) {
linkLast(e);
return true;
}
获取数据时需遍历全部链表匹配,故效率较低。
值得注意的是LinkedList还实现了双向队列,所以也可以作为队列使用
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable
还有LinkedList迭代时可以使用正序或者逆序迭代
public Iterator<E> descendingIterator() {
return new DescendingIterator();
}
3、Vector
很多人有疑问,为什么这个类很少用到,因为这个类是早期JDK1.0时定义的,早期的JDK注重线程安全对效率考虑的并不周全,所以在后续才会出了非线程安全的ArrayList,而Vector是线程安全的。
除了扩容和ArrayList有所不同,还有迭代器也是加了锁以保证线程安全,其他并没有什么区别。
private int newCapacity(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
capacityIncrement : oldCapacity); //不指定为原来的2倍
if (newCapacity - minCapacity <= 0) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return minCapacity;
}
return (newCapacity - MAX_ARRAY_SIZE <= 0)
? newCapacity
: hugeCapacity(minCapacity);
}
4、Stack
Stack基础自Vector,所有它也是线程安全的。源码实现很简单,稍微看一下就好
public class Stack<E> extends Vector<E> {
public Stack() {}
public E push(E item) { //数组尾部添加元素
addElement(item);
return item;
}
public synchronized E pop() {
E obj;
int len = size();
obj = peek();
removeElementAt(len - 1); //删除尾部元素
return obj;
}
public synchronized E peek() { //返回数组尾部元素
int len = size();
if (len == 0)
throw new EmptyStackException();
return elementAt(len - 1);
}
}