List集合

集合底层使用动态数组实现,随机查询效率非常快,插入和删除需要移动整个数组、效率低,ArrayList不是线程安全的、 Vector是线程安全的集合。

Arraylist与Vector集合

相同点:

都是实现与List接口、底层采用数组实现

不同点:

Arraylist线程不安全、Vector线程是安全集合底层采用synchronized保证线程安全问题

Arraylist底层扩容是原数组的50%,Vector集合是原数组的100%

Arraylist底层数组没有默认长度为0,Vector底层数组默认长度为10

Vector集合中可以使用capacityIncrement增长容量

 

ArrayList:

add元素:ensureCapacityInternal进行初始化数组,判断是否需要扩容;

 @Override
    public boolean add(E e) {
        ensureCapacityInternal(size + 1);
        elementData[size++] = e;
        return true;
    }

扩容:是以原有数组的大小扩容50%;扩容的实质就是将原有数组中的数据复制到扩容后的新数组中;扩容最大值为Integer.MAX_VALUE 

private void grow(int minCapacity) {
        //获取原来数组的长度
        int oldCapacity = elementData.length;
        //新的数组长度=原来数组长度+(原来数组长度除以2);
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        //新数组的长度-最小容器<0
        if (newCapacity - minCapacity < 0) {
            // 新数组长度=最小容器长度 (该判断的作用:第一次数组容量初始化使用)
            newCapacity = minCapacity;
        }
        if (newCapacity - MAX_ARRAY_SIZE > 0) {
            newCapacity = hugeCapacity(minCapacity);
        }
        // 使用 Arrays.copyOf 对我们数组容量实现扩容
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

get数据:从数组对应下标取数;

@Override
    public E get(int index) {
        // 从数组中获取到数据
        return (E) elementData[index];
    }

remove数据:将被移除的元素后面的数据全部前移一位;最后一个下标数据置为NULL;

  public E remove(int index) {
        rangeCheck(index);
        E oldValue = get(index);
        // 获取移动下标 剩余后面的长度
        int numMoved = size - index - 1;
        if (numMoved > 0) {
            System.arraycopy(elementData, index + 1, elementData, index, numMoved);//核心
        }
        // 否则情况就是移除最后一个
        elementData[--size] = null;
        return oldValue;
    }

Vector 源码分析:

    1. 默认构造函数中对数组的扩容为长度是为10;(如果创建了集合,没有使用的话是比较占用内存的);
    2. Vector集合实现add方法的时候有synchronized关键字修饰;
    3. Vector集合默认数组扩容是增长一倍,可以自己指定扩容倍数 (使用构造函数传递)

CopyOnWriteArrayList

实现了List接口

内部持有一个ReentrantLock lock = new ReentrantLock();

底层是用volatile transient声明的数组 array

读写分离,写时复制出一个新的数组,完成插入、修改或者移除操作后将新数组赋值给array

public boolean add(E e) {
    // 1.获取原来数组的内容
    Object[] elements = getArray();
    // 2.获取数组的长度
    int len = elements.length;
    // 3.在我们之前的数组基础扩容+1
    Object[] newElements = Arrays.copyOf(elements, len + 1);
    newElements[len] = e;
    // 4.保存我们原来的数组的内容
    setArray(newElements);
    return true;
}

LinkeList

public class MayiktLinkedList<E> implements MayiktList<E> {
    /**
     * 大小
     */
    transient int size = 0;
    /**
     * 链表中第一个元素
     */
    transient MayiktLinkedList.Node<E> first;
    /**
     * 链表中最后的元素
     */
    transient MayiktLinkedList.Node<E> last;

    @Override
    public int size() {
        return size;
    }

    @Override
    public boolean add(E e) {
        linkLast(e);
        return true;
    }

    @Override
    public E get(int index) {
        return (E) node(index).item;
    }

    MayiktLinkedList.Node<E> node(int index) {
        // 如果 当前获取的index <size/2 其实这就是一个折半查找
        if (index < (size >> 1)) {
            // 获取第一个节点
            MayiktLinkedList.Node<E> x = first;
            // 从0开始到当前查询下标位置
            for (int i = 0; i < index; i++) {
                //获取到该元素
                x = x.next;
            }
            return x;
        } else {
            // 获取到最后一个元素
            MayiktLinkedList.Node<E> x = last;
            // 从最后一个元素开始遍历到向左遍历
            for (int i = size - 1; i > index; i--) {
                x = x.prev;
            }
            return x;
        }
    }

    @Override
    public E remove(int index) {
        //获取删除的node节点
        Node<E> node = node(index);
        //获取当前删除节点的下一个
        Node<E> next = node.next;
        //获取当前节点删除的上一个
        Node<E> prev = node.prev;
        if (prev == null) {
            //这说明删除的元素是第一个
            first = next;
        } else {
            prev.next = next;
        }
        if (next == null) {
            // 如果删除当前没有 没有下一个next 说明是最后一个节点,则最后一个节为当前伤处的上一个
            last = prev;
        } else {
            next.prev = prev;
        }
        size--;
        return (E) node;
    }

    void linkLast(E e) {
        // 获取当前最后节点元素
        final MayiktLinkedList.Node<E> l = last;
        // 设置新增的node节点元素
        final MayiktLinkedList.Node<E> newNode = new MayiktLinkedList.Node<>(l, e, null);
        // 最后的节点也就是当前新增的节点
        last = newNode;
        if (l == null) {
            // 说明当前链表是第一次添加元素
            first = newNode;
        } else {
            //当前下一个节点为 新增的节点
            l.next = newNode;
        }
        size++;
    }

    /**
     * 存放节点元素
     *
     * @param <E>
     */
    private static class Node<E> {
        /**
         * 存放节点元素
         */
        E item;
        /**
         * 下一个节点
         */
        MayiktLinkedList.Node<E> next;
        /**
         * 上一个节点
         */
        MayiktLinkedList.Node<E> prev;

        // 设置新增的node节点元素
//        final MayiktLinkedList.Node<E> newNode = new MayiktLinkedList.Node<>(l, e, null);
        // 上一个节点的last节点也就是下一个的节点的上一个节点(prev)
        Node(MayiktLinkedList.Node<E> prev, E element, MayiktLinkedList.Node<E> next) {
            // 节点元素
            this.item = element;
            // 下一个元素
            this.next = next;
            // 上一个元素
            this.prev = prev;
        }
    }
}

 

链表数据底层原理实现:双向链表头尾相接

  1. 在底层中使用静态内部类Node节点存放节点元素

三个属性 pred(关联的上一个节点) item 当前的值 next 下一个节点

  1. Add原理是如何实现 一直在链表之后新增
  2. Get原理 链表缺点:查询效率非常低,所以linkeList中采用折半查找 范围查询定位node节点
  3. 删除原理:就是改变头尾相结合 指向


Arraylist 查询比较多的场景

LinkeList 查询较少,新增该较多;

posted on 2020-05-17 14:56  JAVA-ROAD  阅读(108)  评论(0)    收藏  举报

导航