LinkedList是以链表实现的,在LinkedList里定义了一个为Node的静态内部变量作为节点对象。用于存储集合的前置后置位置和集合元素。
LinkedList定义了三个变量:
transient int size = 0; ---集合的长度
transient Node<E> first; ---第一个元素的位置
transient Node<E> last; 最后一个元素的位置
这里有一个transient 修饰词,就是不可被序列化,所以这三个变量是不可被序列化的。
再来看Node节点对象:
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;
}
}
这是一个静态内部类,里面定义了三个变量,item,这个变量就是用来存集合的元素的,它还定义了两个本类比变量,是用于存储元素前后位置的。
在调用实例化LinkedList无参构造方法时构造对象时,它是没有对任何变量进行初始化的无参构造方法是一个空的方法,所以调用无参构造方法构造LinkedList对象后,对象的三个变量都为初始的默认值,即
size = 0;
transient Node<E> first=null;
transient Node<E> last=null;
在调用add方法时代码如下:
假设我们add(1);
public boolean add(E e) {
linkLast(e);
return true;
}
在这里调用了linkLast(E e)这个方法,e=1,如果不报错,返回true表示添加元素成功。
继续跟进代码查看linkLast方法:
void linkLast(E e) {
final Node<E> l = last;
final Node<E> newNode = new Node<>(l, e, null);
last = newNode;
if (l == null)
first = newNode;
else
l.next = newNode;
size++;
modCount++;
}
在方法里定义了局部变量 l,并将last赋值给了l,此时l=null
然后定义了一个newNode 新节点对象,并通过构造方法进行初始化,将参数l=null,e=1和null传进去
初始化完成后newNode的节点变量的各个参数和LinkedList的参数变化为:
item=1
Node<E> next=null;
Node<E> prev=null;
代码继续往下执行,则
last=newNode。
进行判断如果l=null则说明这是集合的第一个元素,将newNode赋值给first,也就是第一个元素的结合后的变量了。否则不是第一个元素,则将l的赋值给 l 的next对象。这里final就起到了作用了,初始化后,l对象将不可再改变。
继续往下则将集合长度+1,最后改变量也+1.
到此处添加第一个元素就完成了。
那再添加一个2
add(2)或添加n次时,代码还是原来的代码。这个过程的内存变化如下:
对EA等uml软件使用还摸不着门道,暂时先这样画吧。
从这个图可以发现,LinkedList和ArrayList底层是不一样的,添加元素时的内存变化也是不一样的,ArrayList是数组的形式,总是把元素添加到数组的末位,LinkedList来一个元素就创建一个Node对象,两个对象之间又相互有联系,一直排着,就向一条长链条。
这就是LinkedList的底层结构了。可能也不会有人看到我这个文章。