一个菜鸡对ArrayList,LinkedList,Vector常用方法源码的理解

集合

ArrayList

jdk1.8

ArrayList的构造方法

  private static final int DEFAULT_CAPACITY = 10;//初始容量大小
    private static final Object[] EMPTY_ELEMENTDATA = {};//当new数组的时候,如果参数是0,那么返回这个数组
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};//默认返回的数组
    transient Object[] elementData; // non-private to simplify nested class access//保存添加到数组中的元素,集合的实例
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

ArrayList的Add方法

public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!完成数组初始化容量,判断数组是否能放入这个元素,不能扩容
        elementData[size++] = e;
        return true;
    }
    
private void ensureCapacityInternal(int minCapacity) {//minCapacity是数组最小需要的容量
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {//如果是刚new的空集合,数组大小设置为10
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);//三元运算,初始化为10
        }

        ensureExplicitCapacity(minCapacity);
    }
    
private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        // overflow-conscious code
        /**
         * 当minCapacity出现int类型溢出的时候,还可以为true
         * 比如 minCapacity = Integer.MAX_VALUE + 10
         *      elementData.length = Integer.MAX_VALUE -10
         *      就为true,这样就可以进入if循环,然后判断到是int溢出
         *      报错OutOfMemoryError,而不是jdk1.6的ArrayIndexOutOfBoundsException
         *      因为这是超出了jvm对数组大小的限制,应该报Error,而不是exception
         */
        if (minCapacity - elementData.length > 0)//判断是否扩容,
            grow(minCapacity);
    }
    
private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;//原数组容量
        int newCapacity = oldCapacity + (oldCapacity >> 1);//扩容,1.5倍,第二次扩容后是22
        if (newCapacity - minCapacity < 0)//只有第一次加入元素的时候会用到,将数组容量设置为10
            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();//当是int溢出的时候
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }

public static <T> T[] copyOf(T[] original, int newLength) {//original原来数组,newLength扩容后的大小
        return (T[]) copyOf(original, newLength, original.getClass());
    }

public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
        @SuppressWarnings("unchecked")
        T[] copy = ((Object)newType == (Object)Object[].class)
            ? (T[]) new Object[newLength]
            : (T[]) Array.newInstance(newType.getComponentType(), newLength);
        System.arraycopy(original, 0, copy, 0,
                         Math.min(original.length, newLength));//数组元素的复制
        return copy;
    }

ArrayList的set方法

public E set(int index, E element) {
        rangeCheck(index);//检查角标

        E oldValue = elementData(index);
        elementData[index] = element;//将值替换
        return oldValue;//返回旧值
    }

ArrayList的indexof(Object obj)

当obj为null的时候,会用==进行判断元素是否相等,当obj不为null的时候用equalse()判断

jdk1.6.0_04

jdk1.6的ArrayList的构造方法

private transient Object[] elementData;
private int size;

//和jdk1.8不同,它直接就初始化了一个容量为10是数组
public ArrayList() {
	this(10);
    }
public void ensureCapacity(int minCapacity) {
	modCount++;
	int oldCapacity = elementData.length;
	if (minCapacity > oldCapacity) {//这儿在jdk1.8中有改进,这儿的oldCapacity在上一次扩容的时候可能会溢出变成负数,那么金辉进入if判断导致一直扩容
	    Object oldData[] = elementData;
	    int newCapacity = (oldCapacity * 3)/2 + 1;
    	    if (newCapacity < minCapacity)
		newCapacity = minCapacity;
            // minCapacity is usually close to size, so this is a win:
            elementData = Arrays.copyOf(elementData, newCapacity);
	}
    }

ArrayList的总结

1.增删需要拷贝数组
2.jdk1.8默认初始化容量0,第一次add元素的时候变为10;jdk1.6初始化就是容量为10的数组
3.jdk1.8是1.5倍扩容,第二次扩容是22,jdk1.6扩容是(oldCapaty * 3)/2 +1
4.jdk1.6的时候,如果出现int溢出,比如addAdd(ArrayList arr2)arr2的大小加上原来数组的容量大于了Integer.MAX_VALUE就变成负数了,就会出现int溢出,但是1.6没有对int溢出做处理,还是报数组下标越界;jdk1.8的时候判断是否需要扩容的时候是用minCapacity - oldCpacity > 0 而不是 minCapacity > oldCapacity,前者可以判断是否int溢出,如果是溢出的话,还是可以进入if判断中,然后对溢出单独处理,报OutOfMemoryError()
5.jdk1.6的时候,很多都是用父类的通用实现,比如迭代器,但是jdk1.8有了自己的实现,效率更高了

LinkedList

jdk1.8

Node节点的结构

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

构造方法

    transient int size = 0;//大小
    transient Node<E> first;//首节点
    transient Node<E> last;//尾节点
    //jdk1.6上来就new了一个空的节点,jdk1.8使用了懒加载的思想,用的时候再new
    public LinkedList() {
    }

add方法

public boolean add(E e) {
        linkLast(e);
        return true;
    }
    
void linkLast(E e) {
        final Node<E> l = last;
        final Node<E> newNode = new Node<>(l, e, null);//新插入的节点会记录原来的last节点
        last = newNode;//先将新节点设置为last节点
        if (l == null)//如果尾节点为null的话,刚添加的节点就是第一个元素,将first节点设置为刚添加的节点,否则将刚添加的节点设置为尾节点的下一个节点
            first = newNode;
        else
            l.next = newNode;//将原来的尾节点的next节点设置为新加入的节点,实现双向列表
        size++;
        modCount++;
    }

remove(index)方法

public E remove(int index) {
        checkElementIndex(index);//检查数组是否下标越界
        return unlink(node(index));
    }
    
Node<E> node(int index) {
        // assert isElementIndex(index);

        if (index < (size >> 1)) {//如果index小于列表大小的一半,就从头开始遍历,返回对应的node
            Node<E> x = first;
            for (int i = 0; i < index; i++)
                x = x.next;
            return x;
        } else {//r颗index小于列表大小的一半就从尾开始遍历,返回对应的node节点
            Node<E> x = last;
            for (int i = size - 1; i > index; i--)
                x = x.prev;
            return x;
        }
    }
    
E unlink(Node<E> x) {//index对应的节点
        // assert x != null;
        final E element = x.item;
        final Node<E> next = x.next;
        final Node<E> prev = x.prev;

        if (prev == null) {
            first = next;//如果index是第一个节点,那么就将第二个节点设置为第一个节点
        } else {
            prev.next = next;//如果index不是第一个节点,就将index上一个节点的next节点设置为index的下一个节点
            x.prev = null;//index节点对应的上一个节点路径设置为null,便于GC
        }

        if (next == null) {
            last = prev;//如果index对应的节点是尾节点,就将index对应的上一个节点设置为last节点
        } else {
            next.prev = prev;//否则将index下一个节点的prev设置为index的上一个节点
            x.next = null;//将index的下一个节点设置为null,便于GC
        }

        x.item = null;//index节点是数据设置为null
        size--;//链表大小减一
        modCount++;
        return element;
    }

get(index)方法

public E get(int index) {
        checkElementIndex(index);
        return node(index).item;
    }
    
Node<E> node(int index) {
        // assert isElementIndex(index);

        if (index < (size >> 1)) {//如果index小于列表大小的一半,就从头开始遍历,返回对应的node
            Node<E> x = first;
            for (int i = 0; i < index; i++)
                x = x.next;
            return x;
        } else {//r颗index小于列表大小的一半就从尾开始遍历,返回对应的node节点
            Node<E> x = last;
            for (int i = size - 1; i > index; i--)
                x = x.prev;
            return x;
        }
    }

jdk1.6 LinkedList

Entry节点

private static class Entry<E> {
	E element;
	Entry<E> next;
	Entry<E> previous;

	Entry(E element, Entry<E> next, Entry<E> previous) {
	    this.element = element;
	    this.next = next;
	    this.previous = previous;
	}
    }

构造方法

private transient Entry<E> header = new Entry<E>(null, null, null);
    private transient int size = 0;

    /**
     * Constructs an empty list.
初始化的时候header节点的next和previous都是header节点,双向循环列表
     */
    public LinkedList() {
        header.next = header.previous = header;
    }

add方法

public boolean add(E e) {
	addBefore(e, header);
        return true;
    }
/**
*这个方法中,相当于把header当作了双向循环列表的头一个节点(打个比喻,双向循环列表没有头节点是一个闭环),然后header的previous在一直变化指向新加入的节点,新加入的节点的previous指向header
**/
private Entry<E> addBefore(E e, Entry<E> entry) {
	Entry<E> newEntry = new Entry<E>(e, entry, entry.previous);
	newEntry.previous.next = newEntry;//newEntry.previous是header,
	newEntry.next.previous = newEntry;
	size++;
	modCount++;
	return newEntry;
    }

总结

1.jdk1.8中LinkedList是双向列表,jdk1.6中linkedList是一个双向循环列表,从它的构造方法就能看出来,jdk1.6的时候head.next = header.previous = header,如果不是循环列表的话,首尾应该都是null
2.用它的get方法的时候会进行遍历,如果获取元素的index小于链表大小的一半的话,会从头遍历,反之从尾遍历
3.删除节点的时候,会把删除节点的prev和next都设置为null,数据也设置为null,为了GC

vactor

vactor初始化

public Vector() {
        this(10);
    }
    
public Vector(int initialCapacity) {
        this(initialCapacity, 0);
    }
    
public Vector(int initialCapacity, int capacityIncrement) {
        super();
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        this.elementData = new Object[initialCapacity];
        this.capacityIncrement = capacityIncrement;
    }

vactor的add

public synchronized boolean add(E e) {
        modCount++;
        ensureCapacityHelper(elementCount + 1);//检查数组是否需要扩容
        elementData[elementCount++] = e;
        return true;
    }
    
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;
        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);
    }
//Arrays.copyof和ArrayList的差不多

vector大致总结

1.vector是线程安全的,因为它的方法加的synchronized锁
2.vector是二倍扩容,它new出来就是默认为10的数组,不是等到第一次add才去扩容

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值