4、Java集合源码分析-LinkedList

1. 介绍

  1. 实现List、Deque接口,内部使用双向链表实现的列表集合,允许存储任意类型的元素(包括null)。
  2. 和ArrayList一样都是 不同步的,注意多线程环境下保证在外部同步。

和ArrayList一样,也实现了Cloneable、Serializable接口,用于标记可被克隆和序列化。并实现了List接口,表示具备List集合的规范。

和ArrayList不同的是,LinkedList实现了Deque接口,这是一个双向列表,即从该列表的两端都可以进行增删操作。

2. 字段

头节点和尾节点都是一个Node对象,Node是LinkedList的一个内部类,用于记录当前节点存储的元素和记录上个和下个节点。用于构建一条双向链表。

所以LinkedList内部就是使用这种方式构成的双向链表。

3. 构造器

  1. 无参构造

什么都没有。。

  1. 参数为集合的构造器

addAll()的具体实现等后面添加元素的时候加。

我们可以看到,LinkedList构造器在初始化的时候没做什么初始化操作,这是因为它内部是由链表实现的,不需要刻意指定一段连续的内存来存储,它内部两个节点直接通过next、prev的指向来确定的。

4. 添加元素(重点)

在遇到链表相关的操作的时候,最好可以自己画个图辅助理解(上学的时候老师教的)

4.1. 添加到链表尾部

这两个方法都可以将元素插入到链表的尾部,区别是一个有返回值,一个没返回值。

具体操作我们来看linkLast()方法。

分别讨论,假设是第一次添加元素,最简单就是初始化node节点并把该节点设为头节点和尾节点。

如果不是第一次添加元素,我们来画个图看一下过程:

4.2. 添加到链表头部

和添加到链表尾部基本一致

4.3. 添加到指定位置

注意:这里因为需要将新节点插入到两个节点之间,所以双向链表需要有4条链。

我们来看下如何获取到指定索引位置的节点:

4.4. 将集合添加到链表中

这里和将元素添加到指定索引位置基本相似。

如果我们将需要添加的整个集合看作1个节点,则只需要考虑“3个节点”绑定双链即可。

再解决如何将集合链在一起。

5. 删除元素

5.1. 删除最后一个元素

5.2. 删除第一个元素

总结:删除头节点时,看头节点是否有下一个节点,有的话设为头节点,否则链表为空。

5.3. 删除指定索引位置的元素

5.4. 删除指定元素

上述方法删除的是第一次出现的的节点。如果需要删除最后一次出现的,可以使用removeLastOccurrence()方法。

6. 修改元素

7. 查找元素

7.1. 根据索引查找节点

7.2. 根据节点查找索引

  1. 第一个出现的

  1. 最后一个出现的

8. 遍历

8.1. 普通for循环

LinkedList list = new LinkedList();
list.add(1);
list.add(2);
list.add(3);

for (int i = 0; i < list.size(); i++) {
    System.out.println(list.get(i));
}

8.2. 迭代器 & 增强for循环

LinkedList list = new LinkedList();
list.add(1);
list.add(2);
list.add(3);

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

注:这里的迭代器实现方式和ArrayList的基本相同。

区别是获取节点的时候:ArrayList通过数组索引,LinkedList通过记录下个节点。

关于增强for循环,我们知道增强for循环的底层实现就是通过创建迭代器。

LinkedList list = new LinkedList();
list.add(1);
list.add(2);
list.add(3);

for (Object o : list) {
    System.out.println(o);
}

8.3. 效率对比

LinkedList list = new LinkedList();
for (int i = 0; i < 100000; i++) {
    list.add(i);
}

long b1 = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
    list.get(i);
}
System.out.println("普通for耗时:" + (System.currentTimeMillis() - b1));

long b2 = System.currentTimeMillis();
Iterator it = list.iterator();
while (it.hasNext()) {
    it.next();
}
System.out.println("迭代器耗时:" + (System.currentTimeMillis() - b2));

我们发现耗时差距极大,那么这种问题是怎么导致的呢?我们来分析一下。

在上面我们知道了、在普通for循环中使用get(index)获取元素的时候,是需要将其前面的所有元素都要走一遍的。

而使用迭代器,是使用游标记录位置,用next记录记录下个节点,也就是不会再访问前面的元素。

所以,差距才会这么大。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值