ArrayList和LinkedList(源码分析)

ArrayList恐怕是我们在工作中使用最频繁的集合类型了

ArrayList

它的构造方法如下:

	// 默认容量
    private static final int DEFAULT_CAPACITY = 10;

    private static final Object[] EMPTY_ELEMENTDATA = {};

    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    transient Object[] elementData; class access

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

添加元素

    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }

每次添加元素都要计算

    private void ensureCapacityInternal(int minCapacity) {
    // 每次添加元素都要进行计算
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }

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

        // 扩容
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

计算容量

    private static int calculateCapacity(Object[] elementData, int minCapacity) {
    // 第一次添加元素
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        // 扩容为默认容量10
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }

扩容

    private void grow(int minCapacity) {
        // 获取当前数组的容量
        int oldCapacity = elementData.length;
        // 1.5倍的旧容量
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // 拷贝旧数组到新数组
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

所以大家在选择动态数组时,如果要对存储的元素个数有一个预估,那么可以在创建ArrayList时,就使用ArrayList(int initialCapacity)构造器,从而避免频繁扩容,这也是一个比较推荐的做法,如果我没记错的话,Java开发规范也有这个推荐。

LinkedList

LinkedList是双向链表。双链表的结构是指整个链表有一个头结点和一个尾结点,而且每个结点都可以找到自己的前一个结点和自己的下一个结点。

在这里插入图片描述

内部维护一个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;
        }
    }

添加元素

    public boolean add(E e) {
    
    // 添加到链表末尾
        linkLast(e);
        return true;
    }
    void linkLast(E e) {
        // l指向最后一个节点
        final Node<E> l = last;
        
        // 创建一个新node为e,并且前一个node是l
        final Node<E> newNode = new Node<>(l, e, null);
        
        // last指向新创建的节点
        last = newNode;
        
        // l是空的话,新节点同时也是第一个节点
        if (l == null)
            first = newNode;
        else
            l.next = newNode;
            
        // 元素个数++
        size++;
        // 修改次数++
        modCount++;
    }

删除某个元素

就是先遍历找到这个元素,然后调用unlink方法。

    public boolean remove(Object o) {
        if (o == null) {
            for (Node<E> x = first; x != null; x = x.next) {
                if (x.item == null) {
                    unlink(x);
                    return true;
                }
            }
        } else {
            for (Node<E> x = first; x != null; x = x.next) {
                if (o.equals(x.item)) {
                    unlink(x);
                    return true;
                }
            }
        }
        return false;
    }
    E unlink(Node<E> x) {
        // 要被删除的元素
        final E element = x.item;
        // 被删除元素的下一个元素
        final Node<E> next = x.next;
        // 被删除元素的上一个元素
        final Node<E> prev = x.prev;
		// 代表被删除的是第一个节点
        if (prev == null) {
           // 被删除的下一个将成为第一个元素
            first = next;
        } else {
           // 前一个节点的next应该指向被删除节点的后一个节点【断开】
            prev.next = next;
            // 断开与前一个节点的关系
            x.prev = null;
        }
		// 被删除的是最后一个节点
        if (next == null) {
        	// 前一个节点将变成last
            last = prev;
        } else {
        	// 后一个节点的prev应该指向被删除节点的前一个
            next.prev = prev;
            // 断开与后一个节点的关系
            x.next = null;
        }
		// 把要删除的元素释放掉
        x.item = null;
        // 元素个数-1
        size--;
        // 修改次数+1
        modCount++;
        return element;
    }

总体而言,两者的主要差别是顺序表和链表这两种数据结果的差别。顺序表的添加和删除是要大量元素的移动,arraylist还有额外的扩容和复制操作,而链表主要是修改前后节点的关系即可,效率更好。但是数组是通过索引访问元素的,查找效率无疑是更好的。要结合使用场景选择合适的数据结构。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值