ArrayList的原码分析
package list;
import java.util.ArrayList;
/**
* ArrayList原码分析
* 总结一下:ArrayList的底层主要就是维护了一个elementData[]这样一个数组。
* 在我们new ArrayList的时候,这个时候我们的elementData[]还是一个空的数组,
* 当我们第一次调用add方法的时候,我们的elementData[]就会扩容成一个大小为10
* 的数组,当我们添加第11个数字的时候,又再次发生的扩容,扩容的大小为
* (oldCapacity + oldCapacity/2)。
* 在我们new ArrayList的时候,我们也可以指定初始大小;还可以传一个相同类型的数组
*
* 优点:它是根据索引来查询的,所以查找速度也很快。顺序添加也是很方便的。
* 缺点:在从中间删除和插入的时候,就会引起整个数组的变化,会进行数组元素的复制,很浪费时间。
*/
public class ArrayListDemo {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
list.add("d");
list.add("e");
list.get(2);
}
/**
* 在我们new一个ArrayList的时候,我们会给这个容器赋一个初始值,为空
* private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
*
* 这里我们可以看到这个elementData的值为空
* public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; }
*
* 一个返回值为boolean类型的add方法
* public boolean add(E e) {
* 判断是否需要扩容
* ensureCapacityInternal(size + 1); // Increments modCount!!
* 将值存储到size位置,并且size++
* elementData[size++] = e;
* return true;
* }
*
* 首先我们会进入 ensureCapacityInternal这个函数,
* 然后会判断elementData和DEFAULTCAPACITY_EMPTY_ELEMENTDATA是否相等,
* 如果相等就会给我们的容器给大小DEFAULT_CAPACITY的值为10,所以我们的
* 初始容量大小就为10(就是我们第一次使用add方法的时候)
* private void ensureCapacityInternal(int minCapacity) {
* if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
* minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
* }
* ensureExplicitCapacity(minCapacity);
* }
*
* ensureExplicitCapacity这个方法就是判断是否需要扩容,我们扩容的具体
* 操作就在我们的grow中,当我们的 minCapacity - elementData.length > 0
* 的时候,就需要扩容了
* private void ensureExplicitCapacity(int minCapacity) {
*
* 这个modCount++主要就是防止在多线程插入的时候,
* 可能会出现并发修改异常的状况。ConcurrentModificationException
* modCount++;
*
* // overflow-conscious code
* if (minCapacity - elementData.length > 0)
* grow(minCapacity);
* }
*
* 这个就是我们具体的扩容操作了
* private void grow(int minCapacity) {
* // overflow-conscious code
*
* 首先将我们的数组长度赋给oldCapacity
* int oldCapacity = elementData.length;
*
* 然后扩容,具体扩容大小为 oldCapacity + oldCapacity/2
* int newCapacity = oldCapacity + (oldCapacity >> 1);
*
* 然后判断这个新的Capacity是否比我们的允许的最小容器大小都还要小,
* 如果是,那么就让它等于最小的容器大小(minCapacity)
* if (newCapacity - minCapacity < 0)
* newCapacity = minCapacity;
*
* 如果都大于数组允许的最大大小了
* (MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8,Integer.MAX_VALUE=2^31-1)
* 那么就让newCapacity=数组的最大值
* if (newCapacity - MAX_ARRAY_SIZE > 0)
* newCapacity = hugeCapacity(minCapacity);
* // minCapacity is usually close to size, so this is a win:
*
* 最后就是将我们原来的数组(elementData)复制到我们的新的数组中(newCapacity)
* 然后将最终结果存储到elementData中。
* elementData = Arrays.copyOf(elementData, newCapacity);
* }
* private static int hugeCapacity(int minCapacity) {
* if (minCapacity < 0) // overflow
* throw new OutOfMemoryError();
* return (minCapacity > MAX_ARRAY_SIZE) ?
* Integer.MAX_VALUE :
* MAX_ARRAY_SIZE;
* }
*/
}
LinkedList的源码分析
package list;
import java.util.LinkedList;
import java.util.Vector;
/**
* LinkedList的原码分析
* 总结一下:LinkedList的底层主要就是维护了一个双向链表。
* 我们可以定位的插入,可以从头插入(linkedList.addFirst()),
* 也可以从尾部插入(linkedList.addLast())
*
* 优点:它的添加是非常快的
* 缺点:删除,查询都是需要从头结点或者尾结点开始遍历这个链表的
* (当然只会遍历链表长度的一半,看下面的node原码就知道了),
* 包括我们的插入都是如此。
*/
public class LinkedListDemo {
public static void main(String[] args) {
LinkedList<String> linkedList=new LinkedList<>();
linkedList.add("a");
linkedList.add("b");
linkedList.add("c");
linkedList.add("d");
linkedList.add("e");
linkedList.add(3,"f");
linkedList.get(4);
linkedList.remove(2);
System.out.println(linkedList.size());
}
/**
* 在new LinkedList的时候,如果不传任何参数,就默认走空参的构造方法
* public LinkedList() {
* }
*
* 开始add方法
* public boolean add(E e) {
* linkLast(e);
* return true;
* }
*
* 在尾部开始添加数据
* void linkLast(E e) {
*
* 在我们添加第一个数据的时候,l = null
* final Node<E> l = last;
*
* 将我们当前的数据存入一个新的节点中,它的前一个节点为l,
* 后一个节点为null(因为是在尾部插入嘛)
* final Node<E> newNode = new Node<>(l, e, null);
*
* 将newNode 赋值给l,为下一个节点插入做准备
* last = newNode;
*
* 如果l=null(就是第一次插入节点)
* if (l == null)
*
* 将头结点定位当前插入的这个节点
* first = newNode;
* else
*
* 否则,当前节点就是l的下一个节点。
* l.next = newNode;
*
* size++计数,我们的linkedList.size()等等都会用到这个size
* size++;
* modCount++;
* }
*
* 这是linkedList的remove方法,其实就是从头遍历整个链表,
* 然后找到那个匹配的数字,然后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;
* }
*
* unlink方法
* E unlink(Node<E> x) {
* // assert x != null;
* final E element = x.item;
* final Node<E> next = x.next;
* final Node<E> prev = x.prev;
*
* if (prev == null) {
* first = next;
* } else {
* prev.next = next;
* x.prev = null;
* }
*
* if (next == null) {
* last = prev;
* } else {
* next.prev = prev;
* x.next = null;
* }
*
* x.item = null;
* size--;
* modCount++;
* return element;
* }
*
*
* 在我们非顺序添加的时候
* public void add(int index, E element) {
* checkPositionIndex(index);
*
* if (index == size)
* linkLast(element);
* else
*
* //非顺序添加
* 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;
* }
* }
*/
}
Vector原码分析
package list;
import java.util.Vector;
/**
* Vector原码
* 总结:其实Vector的原码和我们的ArrayList差不多,
* 就是它在每一个方法上面都添加了synchronized。保证了线程安全
* 还有一点值得注意的就是它是在new Vector<>()的时候就已经给好了初始值(默认10)。
*/
public class VectorDemo {
public static void main(String[] args) {
Vector<String> vector = new Vector<>();
vector.add("a");
vector.add("b");
vector.add("c");
vector.add("d");
}
/**
* public synchronized boolean add(E e) {
* modCount++;
* ensureCapacityHelper(elementCount + 1);
* elementData[elementCount++] = e;
* return true;
* }
*/
}