Java源码阅读学习后的浅析和感悟(JKD篇)
- 为什么阅读源码
一、感觉现在学习Java就是在学她的各种API如何调用,时间长了就觉得Java很枯燥失去了热情。
二、好奇这些API在底层如何实现的,就比如pow(a,b)难道就是b个a相乘?顺便再记个笔记方便复习。
三、更想学习这些大佬们的思路,到底是什么样的人才能搞出来这些东西!
四、个人原因,可能是感情问题哈哈哈哈哈,我突然发现我在感情上一旦出现情绪波动,唯一能转移情绪的可能就是技术了!
集合框架类
- 为什么会要引入集合
答:为了更方便更高效的存储多个数据。
在初学的时候,我们都知道在集合出现之前方便存储多个数据的是数组,但是它总归有自己的局限性:
1.长度,在存储对象时由于长度的不确定性使得数组不太适用。因为数组一旦指定了长度就无法更改。
2.存储特性,顺序存储且可重复。故对于需要无序的或需要去重的存储就带来了极大的不便。
3.增加、删除,在增删时效率太低。在我们学数据结构中可知,数组是顺序表的结构,数组除了在末尾删除时不需要移动其他元素,在别的地方都需要。
- 集合结构图(部分)
ArrayList集合源码分析
- 扩容机制
如何扩容:
一、ArrayList中创建了一个Object型的数组elementData[],往后的绝大多数操作都是以elementData数组为基础。
二、当ArrayList对象是用ArrayList()方法被创建时,elementData的初始容量为 0,第一次向里面添加元素时,其容量会扩容到 10,如果需要再次扩容,则扩容的容量时当前容量的1.5倍。
三、当ArrayList对象是用ArrayList(int)方法被创建时,则容量为指定容量大小,如果再次扩容,容量是前一次的1.5倍。
下面给出所要用到的方法、其解释和自己学习后的看法。(变量size-->索引)
- 构造方法
// ArrayList(int)
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
//ArrayList()
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
//ArrayList(Collection<? extends E> c)
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;
}
}
- 插入数据
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
public void add(int index, E element) {
rangeCheckForAdd(index);
ensureCapacityInternal(size + 1); // Increments modCount!!
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}
- 关键方法解释(DEFAULTCAPACITY_EMPTY_ELEMENTDATA–>默认为空的elementData数组)
1.private void ensureCapacityInternal(int minCapacity)
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
该方法用于计算elementData的最小容量。
2.private static int calculateCapacity(Object[] elementData, int minCapacity)
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
注:DEFAULT_CAPACITY默认扩容容量为 10
该方法 1.先判断这个数组的容量是否为 0,如果是 0,说明他用了无参构造器创建了该对象,再用了Matn.max(a,b)去最大值的方法返回给函数头。如果不为 0,则直接返回给函数头。
该方法的目的就是为了判别创建该对象时是否指定了其容量。
3.private void ensureExplicitCapacity(int minCapacity)
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
该方法为了明确最小容量是否足够当前的数据进入。
if (minCapacity - elementData.length > 0) 这段话的意思就是如果最小容量(其实就是指当前集合内有效元素的个数)大于了数组的长度说明容量不够了,需要扩容了故他就往下走,走到grow(minCapacity)方法中去。
4.private void grow(int minCapacity)★
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
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);
}
这一步才是真正对其进行扩容的步骤。
这一段的逻辑其实并不难但是很妙!
int oldCapacity = elementData.length 他把数组的长度给了这个扩容前的容量(oldCapacity)
int newCapacity = oldCapacity + (oldCapacity >> 1) 这段话是先把扩容前的容量扩容1.5呗然后赋给扩容后的容量(newCapacity ) 这里的位运算就用的很妙 oldCapacity >> 1 == oldCapacity / 2
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity; 这段话就是进行修补了,如果新容量比最小容量还小,那还不如用这个最小容量。
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity); 这句话应该就是防止数组无限扩容。
elementData = Arrays.copyOf(elementData, newCapacity); 最后用了这个拷贝的方法,他在保留了原有数据的基础上再对数组进行扩容。
5.总结
第一次学习源码,真的是满嘴卧槽,很多地方太妙了。虽然自己的水平太难达到这种地步,但是看大牛前辈们留下的精华也是一种享受!!但是自己觉得这里面也有些不足,比如在用add()方法时,他每次都对当前容量进行判断是否需要扩容,如果我时批量添加元素也就是我添加n次他就判断n次,这样可能就会使得效率略低,做了一些无用功,但是还是不妨碍大佬们的这些发光的技术精华!(个人感受)
LinkedList集合源码分析
- 增删改查分析
LinkedList本质上就是一个(双向)链表,其优点:
一、增加和删除的操作效率高,相比于数组,其时间复杂度为O(n)而链表则为O(1)
二、不受大小限制,拓展性强。
三、内存利用率高,只有在创建时才占用内存,不会对内存造成浪费。
- 链表基本插入删除操作演示(动图)
插入
删除