线性表数据结构源码

160 阅读2分钟

ArrayList源码解读

  • ArrayList是一个集合,对数组进行了二次封装
public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
  • 先看ArrayList的默认构造函数,这里的 elementData 是ArrayList中声明的一个Objet数组
transient Object[] elementData; //声明的一个Object[] 变量
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};//赋值为一个空数组
  • ArrayList的指定容量初始化构造函数
    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);
    }
}
  • ArrayList带参构造函数,可传入一个集合
public ArrayList(Collection<? extends E> c) {
    elementData = c.toArray(); // 将传入的集合转换为对象数组
    if ((size = elementData.length) != 0) {//将数据元素的个数赋值给size 并且判断size不为0
        // c.toArray可能不会返回一个Object[] 这是java的一个bug (bug编号:6260652)
        if (elementData.getClass() != Object[].class)//再判断数据元素的类型如果不是Object[]类型
            elementData = Arrays.copyOf(elementData, size, Object[].class);
    } else {
        // replace with empty array.
        this.elementData = EMPTY_ELEMENTDATA;
    }
}

Hotspot 的一个bug 以下是对.toArray函数不一定返回Object[ ]的描述 image.png 为了重现这个bug,写一个案例验证

image.png 官方说明是 Arrays.asList 之后再toArray 原本应该是一个Object[ ],但返回却不一定是Object[ ]

  • 数组添加元素
public boolean add(E e) {
    ensureCapacityInternal(size + 1);  // 确保容量区间足够,不够要进行扩容
    elementData[size++] = e;
    return true;
}
private void ensureCapacityInternal(int minCapacity) {
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {//判断elementData是不是默认的
        minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);//在传入的容量和默认容量中取最大
    }

    ensureExplicitCapacity(minCapacity); // 确认是否需要扩容
}
private void ensureExplicitCapacity(int minCapacity) {
    //AbstractList中的变量,值为0. ArrayList初始化时 内部类Itr迭代器中expectedModCount获取了它的值
    modCount++;

    // overflow-conscious code
    if (minCapacity - elementData.length > 0)//如果传入的容量大于elementData的长度则进行扩容
        grow(minCapacity);
}
private void grow(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;
    int newCapacity = oldCapacity + (oldCapacity >> 1); //数组扩容 右移1位等于除2
    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);
}

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) {
    final Node<E> l = last; //声明l 指向最后一个节点
    final Node<E> newNode = new Node<>(l, e, null); //构造上一节点为最后节点下节点为null的新节点
    last = newNode;//给链表最后节点赋值为新的节点
    if (l == null) //如果链表的最后节点为null 那么新节点为第一个
        first = newNode;
    else
        l.next = newNode;
    size++;
    modCount++;
}

头插法

public void push(E e) {
    addFirst(e);
}
public void addFirst(E e) {
    linkFirst(e);
}
private void linkFirst(E e) {
    final Node<E> f = first; //声明f 指向第一个节点
    final Node<E> newNode = new Node<>(null, e, f);//构造上一节点为null下节点为最后节点的新节点
    first = newNode; //使链表的第一节点为新的节点
    if (f == null) // 如果链表第一节点为null 那么新节点为第一个
        last = newNode;
    else
        f.prev = newNode;
    size++;
    modCount++;
}

获取节点

public E get(int index) {
    checkElementIndex(index); //校验节点下标是否合法
    return node(index).item;
}
Node<E> node(int index) {
    // assert isElementIndex(index);

    if (index < (size >> 1)) { //如果index小于 size/2 这里其实类似二分查找
        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;
    }
}