Java集合03

8.LinkedList

1)linkedList底层实现了双向链表双端队列的特点

2)可以添加任意元素(元素可以重复),包括null

3)线程不安全,没有实现同步

  • LinkedList的底层操作机制

    1. LinkedList底层维护了一个双向链表
    2. LinkedList中维护了两个属性first和last,分别指向首节点和尾节点
    3. 每个节点(Node对象)里面又维护了prev、next、item三个属性,其中通过prev指向前一个节点,通过next指向后一个节点,最终实现双向链表。
    4. 所以LinkedList的元素的添加和删除不是通过数组完成的,相对来说效率较高

8.1双向链表

例子:实现简单的双向链表

package li.collections.list.linkedlist;

public class LinkedListTest {

    public static void main(String[] args) {
Node jack = new Node("jack");
Node tom = new Node("tom");
Node marry = new Node("marry"); //连接节点,形成双向链表
//jack-->tom-->marry
jack.next = tom;
tom.pre = jack;
tom.next = marry;
marry.pre = tom; Node first = jack;//让头节点指向Jack
Node last = marry;//尾节点指向marry //从头到尾遍历
while (true) {
if (first == null) {
break;
}
System.out.println(first);
first = first.next;
} System.out.println("=========="); //从尾到头遍历
while (true) {
if (last == null) {
break;
}
System.out.println(last);
last = last.pre;
} }
} //定义一个Node对象,表示节点
class Node {
public Object item;//存放数据
public Node next;//指针指向下一个节点
public Node pre;//指针指向上一个节点 public Node(Object name) {
this.item = name;
} @Override
public String toString() {
return "Node item=" + item;
}
}

例子2:双向链表节点的增加和删除

package li.collections.list.linkedlist;

public class LinkedListTest {

    public static void main(String[] args) {
Node jack = new Node("jack");
Node tom = new Node("tom");
Node marry = new Node("marry"); //连接节点,形成双向链表
//jack-->tom-->marry
jack.next = tom;
tom.pre = jack;
tom.next = marry;
marry.pre = tom; Node last = marry;//尾节点指向marry //链表添加数据
//要求在tom和marry之间插入一个对象json
Node json = new Node("json"); //指针先连后删
json.pre = tom;
json.next = marry;
marry.pre = json;
tom.next = json; Node first = jack;//让头节点再次指向Jack //从头到尾遍历
while (true) {
if (first == null) {
break;
}
System.out.println(first);
first = first.next;
} }
} //定义一个Node对象,表示节点
class Node {
public Object item;//存放数据
public Node next;//指针指向下一个节点
public Node pre;//指针指向上一个节点 public Node(Object name) {
this.item = name;
} @Override
public String toString() {
return "Node item=" + item;
}
}

8.2LinkedList底层结构

常用方法:

例子1:LinkedList双向链表增加数据

package li.collections.list.linkedlist;

import java.util.LinkedList;

public class LinkedListCRUD {
@SuppressWarnings("all")
public static void main(String[] args) {
LinkedList linkedList = new LinkedList(); linkedList.add(1);
linkedList.add(2); System.out.println("linkedList="+linkedList); }
}
  1. 在执行LinkedList linkedList = new LinkedList();时,LinkedList底层创建了一个空的列表

  1. 执行linkedList.add(1);时,首先进行自动装箱(略),然后调用底层的add()方法,add()又调用linkLast()方法。



linkLst()方法将新的节点加到双向链表的最后:

首先创建一个node类型的常量 l ,首先将last的地址赋给l,因为刚开始没有数据,last指向null,因此l同样也指向null。

再创建一个新的节点newNode,将add(1)传入的参数赋给newNode,newNode的pre指向l,即指向null,newNode的next也指向null。

然后将last指向newNode

第一次的l指向了null,因此执行first也指向新结点newNode语句

最后是链表长度加1,修改次数modCount加1

所以,第一个节点的时候情况如图:



linkedList.add(2);执行第二次添加操作时,重复上述操作,最后来到linkLast()方法。

  1. 此时last指向的是第一个节点,执行final Node<E> l = last;,则 l 也指向第一个节点。

  2. 再新建一个节点,将 l 设置为新结点的pre,即将新结点的前置节点设置为了第一个节点,将新结点的next设置为空。final Node<E> newNode = new Node<>(l, e, null);

  3. 将last指向新结点

  4. 这里 l不再指向null,因此执行l.next=newNode;语句,因为l这时指向第一个节点,所以执行语句之后第一个节点的next将会指向newNode


例子2:双向链表删除数据

package li.collections.list.linkedlist;

import java.util.LinkedList;

public class LinkedListCRUD {
@SuppressWarnings("all")
public static void main(String[] args) {
LinkedList linkedList = new LinkedList(); linkedList.add(1);
linkedList.add(2);
linkedList.add(3); linkedList.remove();//这里默认删除第一个节点
System.out.println("linkedList="+linkedList);//linkedList=[2, 3] }
}


分析unlinkFirst()方法:将f指向的双向链表中的第一个节点删除

  1. 首先在removeFirst()方法中将 f 指向第一个节点:final Node<E> f = first;

  2. 然后取出第一个节点的值item:final E element = f.item;

  3. 然后将第一个节点next存储的地址赋给常量next,即将next指向第二个节点:final Node<E> next = f.next;

  4. 然后将第一个节点的值置空,以及将第一个节点指向的地址置空,这样第一节点指向第二节点的next就断开了:f.item = null;

    f.next = null; // help GC

  5. 然后将next的地址赋给first,此时的next已经指向了第二个节点,所以first也指向第二个节点:first = next;

  6. 然后判断next是否为空,即判断删除的节点时候为最后一个节点,如果是,则将last置空;若否则将next指向节点的pre置空,也就是将第二个节点指向第一节点的指针断开。

if (next == null)
last = null;
else
next.prev = null;
  1. 而后链表长度-1,链表操作次数+1

  2. 最后返回删除数据的内容

例子3:其他常用方法

package li.collections.list.linkedlist;

import java.util.Iterator;
import java.util.LinkedList; public class LinkedListCRUD {
@SuppressWarnings("all")
public static void main(String[] args) {
LinkedList linkedList = new LinkedList(); linkedList.add(1);
linkedList.add(2);
linkedList.add(3); //修改某个节点对象
linkedList.set(1,999);//public E set(int index, E element)
System.out.println("linkedList="+linkedList);//linkedList=[1, 999, 3] //得到某个节点对象
System.out.println(linkedList.get(1));//得到第二个节点对象 999 //遍历:因为LinkedList实现了List接口,所以LinkedList也可以使用增强for循环和迭代器
// 1.增强for循环
for (Object o:linkedList) {
System.out.println(o); } //2.迭代器
Iterator iterator = linkedList.iterator();
while (iterator.hasNext()) {
Object next = iterator.next();
System.out.println(next);
} //3.普通for循环
for (int i = 0; i < linkedList.size(); i++) {
System.out.println(linkedList.get(i));
} }
}

8.3ArrayList和LinkedList的比较

底层结构 增删的效率 改查的效率
ArrayList 可变数组 较低:数组扩容 较高
LinkedList 双向链表 较高:通过链表追加 较低

如任选择ArrayList和LinkedList:

  1. 如果我们改查比较多,选择ArrayList
  2. 如果增删比较多,选择LinkedList
  3. 一般来说,在程序中百分之80到90都是查询,因此大部分情况下会选择ArrayList
  4. 在一个项目中,根据业务灵活查询,也可以两个集合一起用。一个模块使用ArrayList,另一个模块使用LinkedList

day20--Java集合03的更多相关文章

  1. Java集合03——你不得不了解的Map

    Map 在面试中永远是一个绕不开的点,本文将详细讲解Map的相关内容.关注公众号「Java面典」了解更多 Java 知识点. Map Map 是一个键值对(key-value)映射接口: 映射中不能包 ...

  2. Java集合04——fail-fast&fail-safe 详解

    在前几个回合中,我们已经详细了解过了 Java 集合中的List.Set 和 Map,对这部分内容感兴趣的朋友可以关注我的公众号「Java面典」了解.今天我们将为各位介绍集合的失败机制--fail-f ...

  3. Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例

    java 集合系列目录: Java 集合系列 01 总体框架 Java 集合系列 02 Collection架构 Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例 Java ...

  4. Java多线程系列--“JUC集合”03之 CopyOnWriteArraySet

    概要 本章是JUC系列中的CopyOnWriteArraySet篇.接下来,会先对CopyOnWriteArraySet进行基本介绍,然后再说明它的原理,接着通过代码去分析,最后通过示例更进一步的了解 ...

  5. Java 集合系列04之 fail-fast总结(通过ArrayList来说明fail-fast的原理、解决办法)

    概要 前面,我们已经学习了ArrayList.接下来,我们以ArrayList为例,对Iterator的fail-fast机制进行了解.内容包括::1 fail-fast简介2 fail-fast示例 ...

  6. Java 集合系列目录(Category)

    下面是最近总结的Java集合(JDK1.6.0_45)相关文章的目录. 01. Java 集合系列01之 总体框架 02. Java 集合系列02之 Collection架构 03. Java 集合系 ...

  7. Java集合源码分析(三)LinkedList

    LinkedList简介 LinkedList是基于双向循环链表(从源码中可以很容易看出)实现的,除了可以当做链表来操作外,它还可以当做栈.队列和双端队列来使用. LinkedList同样是非线程安全 ...

  8. Java 集合系列05之 LinkedList详细介绍(源码解析)和使用示例

    概要  前面,我们已经学习了ArrayList,并了解了fail-fast机制.这一章我们接着学习List的实现类——LinkedList.和学习ArrayList一样,接下来呢,我们先对Linked ...

  9. Java 集合系列10之 HashMap详细介绍(源码解析)和使用示例

    概要 这一章,我们对HashMap进行学习.我们先对HashMap有个整体认识,然后再学习它的源码,最后再通过实例来学会使用HashMap.内容包括:第1部分 HashMap介绍第2部分 HashMa ...

随机推荐

  1. 安装Speedtest到CentOS(YUM)

    Speedtest是一个由Python语言编写的一个网络测速脚本,提供多个外网的测试站点,我们可以使用它测试网络的IO速度. 如果由于网络问题导致无法下载软件包,则可以通过安装模块到Python的方式 ...

  2. CoaXPress 是如何只用一条线缆实现双向传输和供电的

    这是个很有意思的事情,CoaXPress的全双工双向数据传输.且供电只需要一条同轴线缆,这个原理对其它串行接口的设计是非常有参考价值的,尤其是对线缆长度.数量有严格要求的场合,一条同轴线缆走天下,不要 ...

  3. 前端向后端传递formData类型的二进制文件

    // 获取到的文件file类型转换为formData类型 let formData = new FormData(); formData.append("file", file文件 ...

  4. Java ES 实现or查询

    es mapping里有三个字段: A:Integer B:Integer C:TEXT 现在想实现一个查询,来检索  (  (A =1 and B=2)  or (c like "test ...

  5. vue跑马灯vue3-marquee

    安装vue3-marquee 如果您使用的是 npm: npm install vue3-marquee@latest --save 如果您使用的是yarn: yarn add vue3-marque ...

  6. 前端5jQuery

    内容概要 jQuery简介 查找标签 jQuery操作标签 jQuery事件操作 jQuery动画效果(了解) 前端第三方框架(基础) 内容详情 jQuery简介

  7. 如何优化PlantUML流程图(时序图)

    这篇文章用来介绍,如何画出好看的流程图. 1. 选择合适的组件 1.1 plantuml官方提供的组件 1.2 加载图片 1.2.1 加载本地图片 1.2.2 加载网络图片 1.2.3 图片资源 2. ...

  8. VMware Workstation 虚拟机详细安装教程

    一.介绍篇 VMware Workstation 16 Pro是VMware(威睿公司)于2021年最新发布的一代虚拟机软件,软件的中文名是"VMware 工作站 16 专业版". ...

  9. 我给航母做3D还原:这三处细节,太震撼了…

    前两天,我国第三艘航母正式下水,受到国际舆论高度关注.国产福建舰火出了圈,"航母"从军事专业领域,也火到了普通人的视野中. 图源网络 人们一边感叹我国实力强劲,一边对"航 ...

  10. Linux字符集和编码

    计算机内部,所有信息最终都是一个二进制值形式存放 字符集 字符集:charset是character set的简写,即二进制和字符的对应关系,不关注最终的存储形式 编码 字符集编码:encoding是 ...