jdk源码有感-ArrayList和LinkedList

·  阅读 65

jdk源码有感-ArrayList和LinkedList

1. ArrayList 底层基于数组实现
定长数组,默认初始长度为10 在项目开发中写代码时最好指定数组长度,尽量减少数组扩容的次数

get(int index) 获取元素,基于内存地址获取,速度很快 add(Object element) 在数组尾部增加一个元素 add(int index,Object element) 在数组中某个位置增加一个元素 remove(int index) 删除数组中的某个位置的元素 set(int index,Object element) 替换数组中某个位置的元素

add(Object element),add(int index,Object element) 会先判断是否需要扩容

private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)
            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);
    }
复制代码

新数组的大小为原数组的1.5倍,用数组拷贝的方法将原数组元素拷贝到新数组中 然后会把原index位置及后面的元素依次向后移动一位

System.arraycopy(elementData, index, elementData, index + 1,size - index);
复制代码

remove(int index) 方法原理与add(int index,Object element)类似,也是把删除位置index之后的元素依次向前移动一位

从这边可以看出,ArrayList对于频繁的插入操作的性能是不太好的,因为会频繁的移动元素,可能还会扩容

2.LinkedList 底层基于双向链表实现

先看一下属性:first是双向链表头结点指针,last是尾结点指针

transient这个关键字是指不参与序列化

/**
     * Pointer to first node.
     * Invariant: (first == null && last == null) ||
     *            (first.prev == null && first.item != null)
     */
    transient Node<E> first;

    /**
     * Pointer to last node.
     * Invariant: (first == null && last == null) ||
     *            (last.next == null && last.item != null)
     */
    transient Node<E> last;
复制代码
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;
        }
    }
复制代码

next指向后面node的指针,prev指向前面node的指针

add(int index, E element):在指定位置新增一个元素, 里面主要是linkBefore方法。node(int index)这个方法是遍历获取index位置的Node结点,分成两部分遍历减少遍历的元素,这代码挺精髓的,自己写代码的时候也可以借鉴这种写法

linkBefore(element, node(index));

Node<E> node(int index) {
        // assert isElementIndex(index);

        if (index < (size >> 1)) {
            Node<E> x = first;
            for (int i = 0; i < index; i++)
                x = x.next;
            return x;
        } else {
            Node<E> x = last;
            for (int i = size - 1; i > index; i--)
                x = x.prev;
            return x;
        }
    }
复制代码

linkBefore方法无非就是把新元素的next指针指向原index位置的Node,prev指针指向原index位置前面的Node,这样就完成了插入操作。可见LinkedList的插入不需要像ArrayList那样需要移动元素,甚至扩容。

void linkBefore(E e, Node<E> succ) {
        // assert succ != null;
        final Node<E> pred = succ.prev;
        final Node<E> newNode = new Node<>(pred, e, succ);
        succ.prev = newNode;
        if (pred == null)
            first = newNode;
        else
            pred.next = newNode;
        size++;
        modCount++;
    }
复制代码

get(int index)其实就是node(int index) 通过遍历获取结点。可见get操作需要通过遍历来获取,性能不如ArrayList通过内容地址获取

public E get(int index) {
        checkElementIndex(index);
        return node(index).item;
    }
复制代码
分类:
后端
标签: