JAVA自带的各种List(基于JDK11)

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);
    }
}

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值