巧言令色,鲜矣仁!

ArrayList源码分析

底层原理:

  1. 创建ArrayList对象的时候,他在底层先创建了一个长度为0的数组。

    数组名字:elementDate,定义变量size

    size这个变量有两层含义:
    ①:元素的个数,也就是集合的长度
    ②:下一个元素的存入位置

  2. 添加元素,添加完毕后,size++ (添加第一个元素时,底层会创建一个新的长度为10的数组)

扩容时机一:

  1. 当存满时候,会创建一个新的数组,新数组的长度,是原来的1.5倍,也就是长度为15.再把所有的元素,全拷贝到新数组中。如果继续添加数据,这个长度为15的数组也满了,那么下次还会继续扩容,还是1.5倍。

扩容时机二:

  1. 如果一次性添加多个数据,扩容1.5倍不够,怎么办呀?

    如果一次添加多个元素,1.5倍放不下,那么新创建数组的长度以实际为准。

举个例子:
在一开始,如果默认的长度为10的数组已经装满了,在装满的情况下,我一次性要添加100个数据很显然,10扩容1.5倍,变成15,还是不够,

怎么办?

此时新数组的长度,就以实际情况为准,就是110

源码阅读

我们这里以ArrayList中的add(E):boolean进行分析

arraylist

LinkedList源码分析

底层原理

LinkedList底层本质上就是一个双链表

核心步骤:

  1. 刚开始创建的时候,底层创建了两个变量:一个记录头结点first,一个记录尾结点last,默认为null
  2. 添加第一个元素时,底层创建一个结点对象,firstlast都记录这个结点的地址值
  3. 添加第二个元素时,底层创建一个结点对象,第一个结点会记录第二个结点的地址值,last会记录新结点的地址值

源码阅读

我们还是以简单的add()方法为例进行分析

首先要明白其基本数据结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
transient int size = 0;  // 表示集合的长度
transient Node<E> first; // 集合的头节点指针
transient Node<E> last; // 集合的尾节点指针


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;
}
}

LinkedList.drawio

迭代器原理分析

底层原理

迭代器的本质就是在ArrayList里面创建一个新的内部类Itr

我们从下面的代码入手

1
2
3
4
5
6
7
8
9
ArrayList<String> list = new ArrayList<String>();
list.add("aaa");
list.add("bbb");
list.add("ccc");

Iterator<String> it = list.iterator();
while(it.hasNext()){
System.out.println(it.next());
}

源码阅读

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
private class Itr implements Iterator<E> {
int cursor; // 迭代器里的指针
int lastRet = -1; // 上一次操作的索引
int expectedModCount = modCount;

Itr() {}

public boolean hasNext() {
return cursor != size; // 如果当前位置的下一个位置还有元素,返回true
}

@SuppressWarnings("unchecked")
public E next() {
checkForComodification();
int i = cursor; // 记录当前指针的位置
if (i >= size) // 如果越界,抛出异常
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData; // 获取当前集合的底部数组
if (i >= elementData.length)
throw new ConcurrentModificationException(); // 抛出并发修改异常
cursor = i + 1; // 指针后移一位
return (E) elementData[lastRet = i]; // 更改lastRet并返回当前元素
}
...
}

写在最后

本文基本上只给出了最简单的方法的源码阅读,是本人第一次阅读java的底层源码,写了很久,中间跨度比较长,倒不是因为这部分内容很难,可能之前算法接触过一些,这些源码都是小卡拉米。刚开始写是在咸宁的时候,如今过去已经20天了,今天刚好回所,所幸是写完了本篇,后面有机会的话会阅读更多的源码。ps:所里环境不错,就是要啥没啥,一会去万达咯,期待一下^ _^