JDK源码学习(jdk1.8)
来源:互联网 发布:淘宝网1 8米纯棉床罩 编辑:程序博客网 时间:2024/06/11 22:23
集合框架
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的定义
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
- JDK源码学习(jdk1.8)
- jdk1.8 LongAdder源码学习
- JDK1.8源码学习之 HashMap.java
- JDK1.8源码学习之Map.java
- JDK1.8源码学习之ConcurrentHashMap.java
- JDK1.8源码学习之HashSet.java
- JDK1.6 源码学习笔记
- ArrayList源码分析(jdk1.8)
- HashMap源码分析(jdk1.8)
- JUC之ReentrantReadWriteLock(JDK1.8源码)
- ArrayList源码分析(JDK1.8)
- ArrayList 源码解读(JDK1.8)
- ArrayList源码分析(JDK1.8)
- LinkedList源码分析(基于jdk1.8)
- hasmap源码分析(jdk1.8)
- TreeMap源码分析(jdk1.8)
- ConcurrentHashMap源码分析(JDK1.8)
- ArrayList源码浅析(jdk1.8)
- java代码参考规范
- I2C
- HashMap全解析
- Python与设计模式(一)——Abstract Factory
- bzoj 1698: [Usaco2007 Feb]Lilypad Pond 荷叶池塘(BFS)
- JDK源码学习(jdk1.8)
- Dubbo 管理页面的部署
- iOS-从任何一个view中获得当前控制器
- CSR是什么样的公司?CSR蓝牙芯片有何过人之处?
- 域名--->IP 和 IP---->MAC
- hdu 6215 Brute Force Sorting
- spring+springmvc+hibernate利用poi实现导出Excel功能
- 关于服务机器人,这都是你不知道的事
- 深度学习里神奇的1*1卷积核