Java LinkedList的实现原理详解

LinkedList是Java List类型的集合类的一种实现,此外,LinkedList还实现了Deque接口。本文基于Java1.8,对于LinkedList的实现原理做一下详细讲解。

(Java1.8源码:http://docs.oracle.com/javase/8/docs/api/

一、LinkedList实现原理总结


LinkedList的实现原理总结如下:

①数据存储是基于双向链表实现的。

②插入数据很快。先是在双向链表中找到要插入节点的位置index,找到之后,再插入一个新节点。 双向链表查找index位置的节点时,有一个加速动作:若index < 双向链表长度的1/2,则从前向后查找; 否则,从后向前查找。

删除数据很快。先是在双向链表中找到要插入节点的位置index,找到之后,进行如下操作:node.previous.next = node.next;node.next.previous = node.previous;node = null 。查找节点过程和插入一样。

④获取数据很慢,需要从Head节点进行查找。

⑤遍历数据很慢,每次获取数据都需要从头开始。


二、ArrayList的实现原理详解


(转自:http://tan4836128.iteye.com/blog/1717809)

1. LinkedList概述:

List 接口的链接列表实现。实现所有可选的列表操作,并且允许所有元素(包括 null)。除了实现 List 接口外,LinkedList 类还为在列表的开头及结尾 get、remove 和 insert 元素提供了统一的命名方法。这些操作允许将链接列表用作堆栈、队列或双端队列。

此类实现 Deque 接口,为 add、poll 提供先进先出队列操作,以及其他堆栈和双端队列操作。

所有操作都是按照双重链接列表的需要执行的。在列表中编索引的操作将从开头或结尾遍历列表(从靠近指定索引的一端)。

注意,此实现不是同步的。如果多个线程同时访问一个链接列表,而其中至少一个线程从结构上修改了该列表,则它必须 保持外部同步。(结构修改指添加或删除一个或多个元素的任何操作;仅设置元素的值不是结构修改。)这一般通过对自然封装该列表的对象进行同步操作来完成。如果不存在这样的对象,则应该使用 Collections.synchronizedList 方法来“包装”该列表。最好在创建时完成这一操作,以防止对列表进行意外的不同步访问,如下所示:

                    List list = Collections.synchronizedList(new LinkedList(...));

此类的 iterator 和 listIterator 方法返回的迭代器是快速失败 的:在迭代器创建之后,如果从结构上对列表进行修改,除非通过迭代器自身的 remove 或 add 方法,其他任何时间任何方式的修改,迭代器都将抛出 ConcurrentModificationException。因此,面对并发的修改,迭代器很快就会完全失败,而不冒将来不确定的时间任意发生不确定行为的风险。

注意,迭代器的快速失败行为不能得到保证,一般来说,存在不同步的并发修改时,不可能作出任何硬性保证。快速失败迭代器尽最大努力抛出 ConcurrentModificationException。因此,编写依赖于此异常的程序的方式是错误的,正确做法是:迭代器的快速失败行为应该仅用于检测程序错误。

 

2. LinkedList的实现:

Java代码  收藏代码
  1. private transient Entry<E> header = new Entry<E>(nullnullnull);   

 

 这个成员变量是LinkedList的关键,它在链表中没有实际数据意义,是链表的标示(通俗一点就是链表的第一个无意义的元素),而且被修饰为transient,标示着他不会被序列化。header也可以当做队列末尾的元素,因为是双向列表,所以header.next末尾元素后边的元素就成了队首元素,header.previous就是队尾元素了,看一下它的添加方法

 

Java代码  收藏代码
  1. public void addFirst(E paramE) {  
  2.     addBefore(paramE, this.header.next);//队首  
  3. }  
  4. public void addLast(E paramE) {  
  5.     addBefore(paramE, this.header);//队尾  
  6. }  

 以上两个方法都利用addBefore方法将元素添加到指定对象之前,

 

 addFirst向队头加元素,将元素paramE添加到header.next-队首元素之前;

 addLast向队尾加元素,将元素paramE添加到header之前;

 

         再看一下addBefore(E e,Entry<E> entry)函数

 

Java代码  收藏代码
  1. /*** 
  2.  * 要添加的元素:paramE 
  3.  * 目标对象:paramEntry 
  4.  */  
  5. private Entry<E> addBefore(E paramE, Entry<E> paramEntry)  
  6. {  
  7.     //要添加的对象  
  8.     Entry localEntry = new Entry(paramE, paramEntry, paramEntry.previous);  
  9.     /*** 
  10.      * localEntry.previous = paramEntry.previous 
  11.      * 目标对象的前一元素的后一元素(localEntry.previous.next)设置为要添加的对象 
  12.      */  
  13.     localEntry.previous.next = localEntry;  
  14.     /*** 
  15.      * localEntry.next = paramEntry 
  16.      * 目标对象的前一元素(localEntry.next.previous)设置为要添加的对象 
  17.      */  
  18.     localEntry.next.previous = localEntry;  
  19.     this.size += 1;  
  20.     this.modCount += 1;  
  21.     return localEntry;  
  22. }  
 

链表的基本特性是插入速度快,遍历速度慢,下面两个方法可以反映这个特点

 

Java代码  收藏代码
  1. public int indexOf(Object paramObject) {  
  2.     int i = 0;  
  3.     Entry localEntry;  
  4.     /*** 
  5.      * 遍历规则:从头到尾,序列呈升序状态 
  6.      */  
  7.     if (paramObject == null)  
  8.         for (localEntry = this.header.next; localEntry != this.header; localEntry = localEntry.next) {  
  9.             if (localEntry.element == null)  
  10.                 return i;  
  11.             i++;  
  12.         }  
  13.     else {  
  14.         for (localEntry = this.header.next; localEntry != this.header; localEntry = localEntry.next) {  
  15.             if (paramObject.equals(localEntry.element))  
  16.                 return i;  
  17.             i++;  
  18.         }  
  19.     }  
  20.     return -1;  
  21. }  
  22. public int lastIndexOf(Object paramObject) {  
  23.     int i = this.size;  
  24.     Entry localEntry;  
  25.     /*** 
  26.      * 遍历规则:从尾到头,序列呈降序状态 
  27.      */  
  28.     if (paramObject == null) {  
  29.         for (localEntry = this.header.previous; localEntry != this.header; localEntry = localEntry.previous) {  
  30.             i--;  
  31.             if (localEntry.element == null)  
  32.                 return i;  
  33.             }  
  34.     }else {  
  35.         for (localEntry = this.header.previous; localEntry != this.header; localEntry = localEntry.previous) {  
  36.             i--;  
  37.             if (paramObject.equals(localEntry.element))  
  38.                 return i;  
  39.         }  
  40.     }  
  41.     return -1;  
  42. }  

 值得注意的是,链表插入数据速度快的说法是相对的,在数据量很小的时候,ArrayList的插入速度不仅不比LinkedList慢,而且还快很多(本文不作介绍,读者可自行测试),只有当数据量达到一定量,这个特性才会体现出来,这需要开发者明确需求场景

LinkedList的方法entry(int index)类似ArrayList的get(int index)

 

Java代码  收藏代码
  1. /*** 
  2.  * 根据序号获取Entry对象 
  3.  */  
  4. private Entry<E> entry(int paramInt) {  
  5.     if ((paramInt < 0) || (paramInt >= this.size)) {  
  6.         throw new IndexOutOfBoundsException("Index: " + paramInt + ", Size: " + this.size);  
  7.     }  
  8.     Entry localEntry = this.header;  
  9.     int i;  
  10.     /*** 
  11.      * 二分法:目标序号小于Size的1/2,则从头到尾 
  12.      *             如果大于Size的1/2,则从尾到头 
  13.      */  
  14.     if (paramInt < this.size >> 1) {  
  15.         for (i = 0; i <= paramInt; i++)  
  16.             localEntry = localEntry.next;  
  17.     } else {  
  18.         for (i = this.size; i > paramInt; i--)  
  19.             localEntry = localEntry.previous;  
  20.     }  
  21.     return localEntry;  
  22. }  

LinkedList还提供了降序迭代器,如下

 

Java代码  收藏代码
  1. public Iterator<E> descendingIterator()   {  
  2.         return new DescendingIterator(null);  
  3.     }  
 

关于降序迭代的具体实现可以看看源码,很简单

LinkedList是针对链表操作的一个比较全的实现,对于频繁的数据插入有较高效率,研究其具体实现可更有利于准确的使用它。该类的toArray、clone以及其他一些方法很值得参考。

 

3. LinkedList.Entry

LinkedList的内部类Entry是实现Deque接口的基本操作单元,其结构如下:

 

Java代码  收藏代码
  1. private static class Entry<E>  
  2. {  
  3.     E element;  
  4.     Entry<E> next;  
  5.     Entry<E> previous;  
  6.     /*** 
  7.      * 构造方法:目标对象paramE将被放置在paramEntry1之前,paramEntry2之后 
  8.      */  
  9.     Entry(E paramE, Entry<E> paramEntry1, Entry<E> paramEntry2)  
  10.     {  
  11.         this.element = paramE;  
  12.         this.next = paramEntry1;  
  13.         this.previous = paramEntry2;  
  14.     }  
  15. }  

4. 内部迭代器:ListItr

虽然上层父类AbstractList<E>已经实现了迭代器,但LinkedList的直接父类AbstractSequentialList<E>给子类重新定义个一个需要实现的迭代器的抽象方法,代码如下:

 

Java代码  收藏代码
  1. public abstract class AbstractSequentialList<E> extends AbstractList<E> {  
  2.     /*** 
  3.      * 返回子类实现的迭代器 
  4.      */  
  5.     public Iterator<E> iterator() {  
  6.         return listIterator();  
  7.     }  
  8.     public abstract ListIterator<E> listIterator(int paramInt);  
  9. }  

 

此处实现的迭代器内部机制跟AbstractList基本一致,可以看看源码

此类的迭代器的实现机制可以通过ListItr的remove方法来分析,同时也可分析Java对双端队列的处理办法

 

Java代码  收藏代码
  1. /*** 
  2.  * 当前指针位置:this.next 
  3.  * 当前所操作的对象:this.lastReturned 
  4.  */  
  5. public void remove() {  
  6.     checkForComodification();  
  7.     LinkedList.Entry localEntry = this.lastReturned.next;  
  8.     try {  
  9.         LinkedList.this.remove(this.lastReturned);  
  10.     } catch (NoSuchElementException localNoSuchElementException) {  
  11.         throw new IllegalStateException();  
  12.     }  
  13.     /*** 
  14.      * 当链表的size为0,指针会指向this.header,其他方法(如set方法)将以此判断当前操作对象的状态 
  15.      */  
  16.     if (this.next == this.lastReturned)  
  17.         this.next = localEntry;  
  18.     else  
  19.         this.nextIndex -= 1;  
  20.     /*** 
  21.      * 调用LinkedList.this.remove(E)之后 
  22.      * 当前操作对象this.lastReturned指向的对象被设置为this.header(原对象为null) 
  23.      * 指针移动到this.lastReturned.next 
  24.      */  
  25.     this.lastReturned = LinkedList.this.header;  
  26.     //修改次数加1  
  27.     this.expectedModCount += 1;  
  28. }  

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/312907.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

知乎高赞:中国有哪些不错的开源软件产品?

点击蓝字“dotNET匠人”关注我哟加个“星标★”&#xff0c;每日 7:15&#xff0c;好文必达&#xff01;在知乎上&#xff0c;有个问题问“中国有什么拿得出手的开源软件产品&#xff08;在 GitHub 等社区受欢迎度较好的&#xff09;&#xff1f;”事实上&#xff0c;还不少呢~…

Java LinkedList双向链表源码分析

LinkedList就传说中的双向链表了。是List 接口的链接列表实现。实现所有可选的列表操作&#xff0c;并且允许所有元素&#xff08;包括 null&#xff09;。除了实现 List 接口外&#xff0c;LinkedList 类还为在列表的开头及结尾 get、remove 和 insert 元素提供了统一的命名方…

容器日志管理 (2) 开源日志管理方案 ELK/EFK

本篇已加入《.NET Core on K8S学习实践系列文章索引》&#xff0c;可以点击查看更多容器化技术相关系列文章。上一篇《容器日志管理&#xff08;1&#xff09;》中介绍了Docker自带的logs子命令以及其Logging driver&#xff0c;本篇将会介绍一个流行的开源日志管理方案ELK/EFK…

Java ArrayList、LinkedList和Vector的使用及性能分析

第1部分 List概括 List 是一个接口&#xff0c;它继承于Collection的接口。它代表着有序的队列。 AbstractList 是一个抽象类&#xff0c;它继承于AbstractCollection。AbstractList实现List接口中除size()、get(int location)之外的函数。 AbstractSequentialList 是一个抽象类…

关于Scrum起源,读这一篇论文就足够啦!《新新产品开发游戏》

关于Scrum的起源&#xff0c;我们经常会提到1986年发表在HBR上的一篇论文&#xff0c;《The New New Product Development Game》&#xff0c;今天我们把它重新翻译&#xff0c;一起重温为何Scrum会如此设置3355&#xff1f;为何会用橄榄球的术语来代表Scrum&#xff1f;The Ne…

Java HashMap的实现原理详解

HashMap是Java Map类型的集合类中最常使用的&#xff0c;本文基于Java1.8&#xff0c;对于HashMap的实现原理做一下详细讲解。 &#xff08;Java1.8源码&#xff1a;http://docs.oracle.com/javase/8/docs/api/&#xff09; 一、HashMap实现原理总结 HashMap的实现原理总结如下…

ASP.NET Core快速入门(第5章:认证与授权)--学习笔记

点击蓝字关注我们课程链接&#xff1a;http://video.jessetalk.cn/course/explore良心课程&#xff0c;大家一起来学习哈&#xff01;任务31&#xff1a;课时介绍1.Cookie-based认证与授权2.Cookie-based认证实现3.Jwt认证与授权介绍4.Jwt认证与授权实现5.Jwt认证与授权6.Role …

Java HashSet的实现原理详解

HashSet是Java Map类型的集合类中最常使用的&#xff0c;本文基于Java1.8&#xff0c;对于HashSet的实现原理做一下详细讲解。 &#xff08;Java1.8源码&#xff1a;http://docs.oracle.com/javase/8/docs/api/&#xff09; 一、HashSet实现原理总结 HashSet的实现原理总结如下…

asp.net mvc 自定义 pager 封装与优化

asp.net mvc 自定义 pager 封装与优化Intro之前做了一个通用的分页组件&#xff0c;但是有些不足&#xff0c;从翻页事件和分页样式都融合在后台代码中&#xff0c;到翻页事件可以自定义&#xff0c;再到翻页和样式都和代码分离&#xff0c; 自定义分页 pager 越来越容易扩展了…

Java LinkedHashMap的实现原理详解

1. LinkedHashSet概述&#xff1a; LinkedHashSet是具有可预知迭代顺序的Set接口的哈希表和链接列表实现。此实现与HashSet的不同之处在于&#xff0c;后者维护着一个运行于所有条目的双重链接列表。此链接列表定义了迭代顺序&#xff0c;该迭代顺序可为插入顺序或是访问顺序…

.net core 中通过 PostConfigure 验证 Options 参数

.net core 中通过 PostConfigure 验证 Options 参数Intro在 .net core 中配置项推荐用 Options 来实现&#xff0c;有一些参数可能必须是用由用户来配置&#xff0c;不能直接写成默认值的参数&#xff0c;这样就需要就 Options 中的参数做一些校验&#xff0c;否则程序内部可能…

Spring配置错误java.lang.NoClassDefFoundError: org/springframework/jdbc/datasource/TransactionAwareDataS

在对Spring数据源dataSource配置之后&#xff0c;运行程序出现如下错误&#xff1a; java.lang.NoClassDefFoundError: org/springframework/jdbc/datasource/TransactionAwareDataSourceProxy 原因是项目没有导入spring-jdbc的jar包。 如果使用maven&#xff0c;可以直接在pom…

.NET做人脸识别并分类

前言在游乐场、玻璃天桥、滑雪场等娱乐场所&#xff0c;经常能看到有摄影师在拍照片&#xff0c;令这些经营者发愁的一件事就是照片太多了&#xff0c;客户在成千上万张照片中找到自己可不是件容易的事。在一次游玩等活动或家庭聚会也同理&#xff0c;太多了照片导致挑选十分困…

Java连接Mysql数据库警告:Establishing SSL connection without server's identity verification is not recommend

Java使用mysql-jdbc连接MySQL出现如下警告&#xff1a; Establishing SSL connection without servers identity verification is not recommended. According to MySQL 5.5.45, 5.6.26 and 5.7.6 requirements SSL connection must be established by default if explicit opt…

.NET Core 3.0中用 Code-First 方式创建 gRPC 服务与客户端

.NET Core ❤ gRPC千呼万唤的 .NET Core 3.0 终于在 9 月份正式发布&#xff0c;在它的众多新特性中&#xff0c;除了性能得到了大大提高&#xff0c;比较受关注的应该是 ASP.NET Core 3.0 对 gRPC 的集成了。它的源码托管在 grpc-dotnet 这个 Github 库中&#xff0c;由微软 .…

Spring集成Mybatis错误Result Maps collection already contains value for XXX

Spring在集成Mybatis出现如下错误&#xff1a; SpringResult Maps collection already contains value for com.guowei.maven.framework.dao.UserMapper.resultUser at org.mybatis.spring.SqlSessionFactoryBean.buildSqlSessionFactory(SqlSessionFactoryBean.java:468) at o…

dotnet Blazor 用 C# 控制界面行为

微软很久就在做 Blazor 但是我现在才开始创建一个测试项目&#xff0c;我想用 C# 去控制 HTML 界面。小伙伴也许会问现在前端不是烂大街么&#xff0c;为什么还需要 Blazor 来做。可能原因只有一个&#xff0c;就是可以使用 C# 写脚本&#xff0c;代码比较清真用 VisualStudio …

Spring集成Mybatis配置映射文件方法详解

Spring ORM模块集成Mybatis使用到了mybatis-spring&#xff0c;在配置mybatis映射文件的时候&#xff0c;一般不直接在Mybatis的配置文件里进行配置&#xff0c;而会在Spring的配置文件里使用MapperScannerConfigurer来配置。MapperScannerConfigurer会自动扫描basePackage指定…

2019年该学习哪门语言?建议学习C#语言

世界上只有少数几种语言是多功能的&#xff0c;而没有一个像C#那样干净整洁。作者 | Arctek译者 | 谭开朗&#xff0c;责编 | 郭芮出品 | CSDN&#xff08;ID&#xff1a;CSDNnews&#xff09;以下为译文&#xff1a;最直接的答案是&#xff1a;值得。但我想你不是来找这样的答…

Spring连接mysql数据库错误:Cannot load JDBC driver class '${driver}'

在用Spring使用连接mysql数据库时出现如下错误&#xff1a; Cause: org.springframework.jdbc.CannotGetJdbcConnectionException: Could not get JDBC Connection; nested exception isjava.sql.SQLException: Cannot load JDBC driver class ${driver} 错误详细信息如下&…