JDK源码学习(jdk1.8)

来源:互联网 发布:淘宝网1 8米纯棉床罩 编辑:程序博客网 时间:2024/06/11 22:23

集合框架

ArrayList

关注点 结论 ArrayList是否允许空 允许 ArrayList是否允许重复数据 允许 ArrayList是否有序 有序 ArrayList是否线程安全 非线程安全

ArrayList的定义

public class ArrayList<E> extends AbstractList<E>        implements List<E>, RandomAccess, Cloneable, java.io.Serializable

ArrayList的底层使用Object数组来存储的

transient Object[] elementData;

以前听说ArrayList的初始大小为10,应该是以前的版本,我这个版本中ArrayList如果不指定大小时默认为空

private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};public ArrayList() {    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;}

在这里主要关注一下自动扩容的实现

public boolean addAll(Collection<? extends E> c) {    Object[] a = c.toArray();    int numNew = a.length;    //确保数组大小超过size + numNew    ensureCapacityInternal(size + numNew);    //实现数组复制,a是源数组,0是源数组要复制的起始位置,elementData是目的数组    //size是目的数组放置的起始位置,numNew复制的长度    System.arraycopy(a, 0, elementData, size, numNew);    size += numNew;    //如果c为空则返回false,不为空则返回true    return numNew != 0;}
public boolean add(E e) {    ensureCapacityInternal(size + 1);  // Increments modCount!!    elementData[size++] = e;    return true;}

增加一个元素或者增加好几个元素都会在调用ensureCapacityInternal方法来确保数组大小足够大,可以确定在ensureCapacityInternal方法里面实现了自动扩容

private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};private static final int DEFAULT_CAPACITY = 10;
private void ensureCapacityInternal(int minCapacity) {    //目前数组为空    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {        //要求的容量大小和10取最大值        minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);    }    ensureExplicitCapacity(minCapacity);}
private void ensureExplicitCapacity(int minCapacity) {    modCount++;    //这个是出于2种考虑    //如果需要的容量大于目前的容量,就需要扩容    //如果minCapacity溢出,即minCapacity为负数,就不扩容    //minCapacity为负数的一种情况是addAll(Collection<? extends E> c)传入的参数已经溢出了    // overflow-conscious code    if (minCapacity - elementData.length > 0)        grow(minCapacity);}
//之所以定义为Integer.MAX_VALUE - 8,是因为VM会保留一些头结点在数组开头private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
private void grow(int minCapacity) {    // overflow-conscious code    int oldCapacity = elementData.length;    //>>是右移运算符,相当于除以2,因此新容量是旧容量的1.5倍    int newCapacity = oldCapacity + (oldCapacity >> 1);    //这里需要分为2种情况    //1.扩容还不满足,直接将新容量设为需要的容量    //2.newCapacity溢出,变为负数,扩充容量扩的太大了    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);}
private static int hugeCapacity(int minCapacity) {    //溢出了,minCapacity的值大于int能存储的最大值,用int存储会变为负数    //这里minCapacity之所以为负数,可能是因为vm进行了一些操作    if (minCapacity < 0) // overflow        throw new OutOfMemoryError();    return (minCapacity > MAX_ARRAY_SIZE) ?        Integer.MAX_VALUE :        MAX_ARRAY_SIZE;}

LinkedList

关注点 结论 LinkedList是否允许空 允许 LinkedList是否允许重复数据 允许 LinkedList是否有序 有序 LinkedList是否线程安全 非线程安全

LinkedList的定义

public class LinkedList<E>    extends AbstractSequentialList<E>    implements List<E>, Deque<E>, Cloneable, java.io.Serializable

LinkedList采用的双向链表结构,因此在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;    }}

定义的变量为

//链表的长度transient int size = 0;//链表的头结点transient Node<E> first;//链表的尾结点transient Node<E> last;

增加元素的方法,addFirst,addLast,add都很类似,分析一个

public void addFirst(E e) {    linkFirst(e);}
private void linkFirst(E e) {    //把链表的头结点放到f这个中途变量    final Node<E> f = first;    //构造结点,前向指针为null,元素为e,后向指针指到f    final Node<E> newNode = new Node<>(null, e, f);    //插入的结点编程头结点    first = newNode;    //如果原先头结点为空    if (f == null)        //构造的结点不仅为头结点,也是尾结点        last = newNode;    else        //原先头结点的前向指针指向构造的结点        f.prev = newNode;    //元素个数加1    size++;    //每次对元素进行操作时,这个都会增加1,主要为了迭代器校验错误使用    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;}
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.prev和x.next赋值为null,LZ猜是为了加速垃圾回收,但没有实际测试    x.item = null;    size--;    modCount++;    return element;}

获取元素的方法

public E get(int index) {    //检查下标是否越界    checkElementIndex(index);    return node(index).item;}
Node<E> node(int index) {    // assert isElementIndex(index);    //size >> 1和size / 2一个效果,因为将位右移一位相当于除2    //在前半段,正向遍历    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;    }}

分析源码可知,通过get方式遍历LinkedList是一种很低效的方式,因为每找一个元素都是从头或者尾开始的,最好采用foreach或迭代器方式

归并排序

这里写图片描述
Java实现

import java.util.Arrays;public class MergeSort {    public static void main(String[] args) {        int[] arr = {7,5,3,4,2,1,6,2,9,8};        sort(arr);        //[1, 2, 2, 3, 4, 5, 6, 7, 8, 9]        System.out.println(Arrays.toString(arr));    }    public static void sort(int[] src) {        int[] temp =  new int[src.length];        msort(src,temp,0,src.length-1);    }    public static void msort(int[] src,int[] dest,int left,int right) {        if (left < right) {            int mid = (left + right) / 2;            msort(src,dest,0,mid);            msort(src,dest,mid+1,right);            merge(src,dest,0,mid,right);        }    }    public static void merge(int[] src,int[] dest,int left,int mid,int right) {        int i = left;//左边数组的游标        int j = mid + 1;//右边数组的游标        int index = 0;//dest起一个中途存储的作用,这个是dest数组的游标        while (i <= mid && j <= right) {            if (src[i] <= src[j]) {                dest[index++] = src[i++];            } else {                dest[index++] = src[j++];            }        }        //复制左边剩余的数组        while (i <= mid) {            dest[index++] = src[i++];        }        //复制右边剩余的数组        while (j <= right) {            dest[index++] = src[j++];        }        index = 0;        while (left <= right) {            src[left++] = dest[index++];        }    }}

参考博客

归并排序
[1]http://www.cnblogs.com/chengxiao/p/6194356.html
>>和>>>的区别
[1]http://blog.csdn.net/blog_szhao/article/details/23997881
modCount的作用
[1]http://blog.csdn.net/u012926924/article/details/50452411
各种链表
[1]http://blog.csdn.net/fisherwan/article/details/25796625
LinkedList源码
[1]http://blog.csdn.net/jzhf2012/article/details/8540543

原创粉丝点击