ArrayList 源码阅读和LinkedList源码阅读 比较笔记

基于jdk 1.8 ArrayList 源码 和 LinkedList 源码

ArrayList 的底层结构是数组的存储结构 LinkedList是基于链表的存储结构 他们都是线程不安全的集合

构造方法的比较

ArrayList的源码

// 这种构造在initialCapacity 大于0 的时候回直接初始化,说明底层的存储结构是数组,查询根据下标进行索引很快
 public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
            this.elementData = EMPTY_ELEMENTDATA;
            //private static final Object[] EMPTY_ELEMENTDATA = {}
        } else {
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
}
//没有指定容器的大小,默认为空数组,懒加载模式
 public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
        //private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
}


 public ArrayList(Collection<? extends E> c) {
        elementData = c.toArray();//复制成一个新的数组
        if ((size = elementData.length) != 0) {
            // c.toArray might (incorrectly) not return Object[] (see 6260652)
            if (elementData.getClass() != Object[].class)
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
            // replace with empty array.
            this.elementData = EMPTY_ELEMENTDATA;
        }
}

LinkedList 的源码

//成员变量 头结点
 transient Node<E> first;
 //成员变量 尾结点
 transient Node<E> last;

 public LinkedList() {
}
public LinkedList(Collection<? extends E> c) {
    this();
    addAll(c);//将别的集合的内容添加到当前的list中
}
//线程不安全
public boolean addAll(Collection<? extends E> c) {
        return addAll(size, c);
}
//说明底层的结构是链表的形式
public boolean addAll(int index, Collection<? extends E> c) {
        checkPositionIndex(index);//检查 index 的合法性

        Object[] a = c.toArray();//先转换成数组
        int numNew = a.length;
        if (numNew == 0)
            return false;

        Node<E> pred, succ;
        if (index == size) {
            succ = null;
            pred = last;
        } else {
            succ = node(index);//使用循环 找到对应下标的节点
            pred = succ.prev;
        }

        for (Object o : a) {
            @SuppressWarnings("unchecked") E e = (E) o;
            Node<E> newNode = new Node<>(pred, e, null);
            if (pred == null)
                first = newNode;// transient Node<E> first;成员变量
            else
                pred.next = newNode;
            pred = newNode;
        }

        if (succ == null) {
            last = pred;
        } else {
            pred.next = succ;
            succ.prev = pred;
        }

        size += numNew;
        modCount++;
        return true;
}

 private void checkPositionIndex(int index) {
        if (!isPositionIndex(index))
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
private boolean isPositionIndex(int index) {
        return index >= 0 && index <= size;
}


//关于节点的类 可以看出LinkedList是一个双向的链表
  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;
        }
}

小结:

  1. ArraList 底层的存储结构是数组,当没有指定容器的大小的时候,默认的数组是一个空的数组。类似于懒加载的模式,只有在用到的时候才会去初始化数组容器。如果创建List的时候指定容器的大小那么就会创建指定打下的数组,如果传进一个集合,那么数组的大小就是集合内元素数量的大小。可以确认的是这种结构通过下标获取元素的速度是最快的,但是对于数组的扩容和插入就比较慢
  2. LinkedList 通过构造方法可以知道,底层是使用双向链表的存储结构来存储数据的,List对象的内部保存了头尾结点。这种结构存储数据会发现获取指定的下标的速度相对于ArrayList【O(1)】是比较慢的,每次寻找基本需要遍历一次,但是相对而言,插入的速度是比较快的,只需要更改一下引用的地址就好了。也没有扩容的限制
增删方法的比较

ArrayList 的源码

public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!! //检查容量是否足够并扩容
        elementData[size++] = e;
        return true;
}
private void ensureCapacityInternal(int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        //private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        // private static final int DEFAULT_CAPACITY = 10;
        }

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

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);//扩容,elementData已经不够存放了
}
 private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        //新数组的大小是原数组的1.5倍,即扩大0.5倍,正常的情况下
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
           //private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
        // minCapacity is usually close to size, so this is a win:
        elementData = Arrays.copyOf(elementData, newCapacity);//创建一个新数组并复制内容
}
//说明数组length最大是Integer.MAX_VALUE
private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
}
//指定位置插入值
public void add(int index, E element) {
        rangeCheckForAdd(index);//index 的范围检查,检查是否正常

        ensureCapacityInternal(size + 1);  // Increments modCount!!
        //需要移动数组的内容
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
        elementData[index] = element;
        size++;
}


public E remove(int index) {
        rangeCheck(index);

        modCount++;
        E oldValue = elementData(index);

        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        //每次删除完数据都可能需要移动数组的内容。
        elementData[--size] = null; // clear to let GC do its work

        return oldValue;
}
//直接根据下标索引
 public E get(int index) {
            rangeCheck(index);
            checkForComodification();
            return ArrayList.this.elementData(offset + index);
}

LinkedList的源码

 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 = newNode;
        if (l == null)
            first = newNode;
        else
            l.next = newNode;
        size++;
        modCount++;
}
//基于链表的形式会发现增加和删除都是比较快的操作
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;
}
// 通过索引获取基本是遍历获取了
public int indexOf(Object o) {
        int index = 0;
        if (o == null) {
            for (Node<E> x = first; x != null; x = x.next) {
                if (x.item == null)
                    return index;
                index++;
            }
        } else {
            for (Node<E> x = first; x != null; x = x.next) {
                if (o.equals(x.item))
                    return index;
                index++;
            }
        }
        return -1;
}
//------------下面是一些关于队列的操作方法------
//返回第一个元素,不移除
 public E peek() {
        final Node<E> f = first;
        return (f == null) ? null : f.item;
}
//也是返回第一个元素,不移除,但是链表为空时,会抛异常
 public E element() {
        return getFirst();
}
public E getFirst() {
        final Node<E> f = first;
        if (f == null)
            throw new NoSuchElementException();
        return f.item;
}
// 弹出第一个元素,并移除
 public E poll() {
        final Node<E> f = first;
        return (f == null) ? null : unlinkFirst(f);
}
//添加 还有很多就不一一介绍了,后面可能会有相关的的博客会详细说
public boolean offer(E e) {
        return add(e);
}

小结

  1. ArrayList 每次扩容是上次容量的1.5倍,最大是int的最大值。list根据下标来查找数据是比较快的,但是增加和删除数据是比较慢的,所以如果是数据只是用于频繁的读话,用ArrayList是比较好的选择。还有创建数组的时候如果大概知道需要存放的数量最好先赋值,这样能减少频繁扩容的操作。在使用对指定的下标的数据进行操作的时候,最好先确认容器内元素的数量,不然是会抛异常的,并且这是线程不安全的集合。Vertor虽然是线程安全,但是基本都是方法级别上(指的是在方法上加synchronized)。(后面会继续出线程安全的集合操作的源码阅读,比如CopyOnWriteArrayList)最后一点可以存储null
  2. LinkedList 本质上来说是容量是没有上限的,因为是双向链表,所以可以一直添加下去,并且对于添加的和删除的操作相对list来说是很快的,只需要修改一下引用就好。但是对于查询就是灾难了,如果链表比较长,而又是遍历的去查找…,最后linkedList 也是线程不安全的list 也可以存储null,linkedList还是一个队列
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值