`
masterzs
  • 浏览: 17399 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

JDK学习之LinkedList

阅读更多

1.先看下LinkedList 的继承关系:

 

 

2. 我们看到其主要实现了List,Queue接口,通过继承一个模板类AbstractSequentialList来实现链表,首先看下成员变量:

    private transient Entry<E> header = new Entry<E>(null, null, null);
    private transient int size = 0;

 

可以看到,有两个成员变量,一个是Entry-静态内部类,还有一个size,代表链表的实际大小,看下Entry:

    private static class Entry<E> {
	E element;
	Entry<E> next;
	Entry<E> previous;

	Entry(E element, Entry<E> next, Entry<E> previous) {
	    this.element = element;
	    this.next = next;
	    this.previous = previous;
	}
    }

 

是一个静态内部类,包含三个成员变量,element-数据项,next-指向下一条数据项,previous-指向前一条数据项,可以看出这是一个双向链接,每个节点

都会指向前一个节点和后一个节点。

这里简要说明一下为什么要使用静态内部类:

摘自网络:

  • 静态内部类的实例不会自动持有外部类的特定实例的引用, 因此在创建内部类的实例时, 不必创建外部类的实例.
  •  静态内部类可以直接访问外部类的静态成员, 如果要访问外部类的实例成员, 就必须通过外部类的实例去访问
  • 静态内部类中可以定义静态成员和实例成员
  •  客户类可以通过完整的类名直接访问静态内部类的静态成员. 
  •  

    那这里,我认为是方便外部类访问,同时因为不包含任何方法,可以不用拿出来单独形成一个类,其他的好处,暂时还没看出来:(

     

    下边重点看下几个方法的实现:

    增加一条记录,add(E o),实现代码:

     

        public boolean add(E o) {
    	addBefore(o, header);
            return true;
        }

     

     

     内部调用了addBefore,再看addBefore的实现方式:

        private Entry<E> addBefore(E o, Entry<E> e) {
    	//创建一个新的Entry实例
    	Entry<E> newEntry = new Entry<E>(o, e, e.previous);
    	//将header前一条数据的next指向newEntry,同时把header的previous指向newEntry,
    	newEntry.previous.next = newEntry;
    	newEntry.next.previous = newEntry;
    	//长度和修改计数递增
    	size++;
    	modCount++;
    	return newEntry;
        }

     在指定位置添加:

        public void add(int index, E element) {
            addBefore(element, (index==size ? header : entry(index)));
        }

      

     

     

    传递参数为:待添加的数据项O和header引用,详细分析参见注释,这里我们可以清晰的看到如何在一个链表结构中添加一条记录。

    删除一条记录,remove(Object c):

     public boolean remove(Object o) {
         //判断插入数据项是否为null
            if (o==null) {
                for (Entry<E> e = header.next; e != header; e = e.next) {
                    if (e.element==null) {
                        remove(e);
                        return true;
                    }
                }
            } else {
                for (Entry<E> e = header.next; e != header; e = e.next) {
                    if (o.equals(e.element)) {
                        remove(e);
                        return true;
                    }
                }
            }
            return false;
        }

     

     再看 remove(e)方法:

     private E remove(Entry<E> e) {
       if (e == header)
           throw new NoSuchElementException();
           //提取待删除数据作为返回值
              E result = e.element;
              /*将待删除数据的next付给前一条数据项的next
               * 将待删除数据的previous指向后一条数据previous
               * 同时释放待删除数据项的next和previous链接,是否element
               */
              
       e.previous.next = e.next;
       e.next.previous = e.previous;
              e.next = e.previous = null;
              e.element = null;
              //总条数size和修改计数递减
       size--;
       modCount++;
              return result;
    }

     

     这里需要注意的是我们需要释放待删除节点的next和previous链接 ,以便于JVM在适当的时候进行垃圾回收。

    获取数据,get(int index):

      public E get(int index) {
            return entry(index).element;
        }

     

     

     我们看下entry方法:

    	  private Entry<E> entry(int index) {
    		  //判断边界--前置条件判断
    	        if (index < 0 || index >= size)
    	            throw new IndexOutOfBoundsException("Index: "+index+
    	                                       ", Size: "+size);
    	        //如果index小于size,则向前遍历,否则向后遍历 
    	        Entry<E> e = header;
    	        if (index < (size >> 1)) {
    	            for (int i = 0; i <= index; i++)
    	                e = e.next;
    	        } else {
    	            for (int i = size; i > index; i--)
    	                e = e.previous;
    	        }
    	        return e;
    	    }

     

    这里我们看到在链表中取值时,需要遍历整个链表,相对于ArrayList的随机访问,会有所缓慢

     

    最后看下contails(Object o)

        public boolean contains(Object o) {
            return indexOf(o) != -1;
        }

     看下indexOf(Object c)

     

     public int indexOf(Object o) {
    	        int index = 0;
    	        if (o==null) {
    	            for (Entry e = header.next; e != header; e = e.next) {
    	                if (e.element==null)
    	                    return index;
    	                index++;
    	            }
    	        } else {
    	            for (Entry e = header.next; e != header; e = e.next) {
    	                if (o.equals(e.element))
    	                    return index;
    	                index++;
    	            }
    	        }
    	        return -1;
    	    }

     

     

     这里分两种情况进行遍历,跟删除是相同的。

     

    3.典型应用:

    暂无

    4,优点与缺点

    优点:

    LinkedList的中间插入或删除一个元素的开销是固定的

    缺点:

     LinkedList不支持高效的随机元素访问

     

    最后,我感觉平时做项目ArrayList还是相对比较常用,LinkedList很少用,不知道有没有人在实际项目中使用过LinkedList ,切实的体会

    到两者之间的差异,而不是向我最后分析的优缺点那样 都是些理论写的说辞,希望大家补充,谢谢。

     

    • 大小: 21.8 KB
    3
    0
    分享到:
    评论

    相关推荐

      基于Springboot的实验报告系统源码数据库文档.zip

      基于Springboot的实验报告系统源码数据库文档.zip

      ERA5_Climate_Single_Month.txt

      GEE训练教程——Landsat5、8和Sentinel-2、DEM和各2哦想指数下载

      基于springboot智能健康饮食系统源码数据库文档.zip

      基于springboot智能健康饮食系统源码数据库文档.zip

      基于SpringBoot的校园服务系统源码数据库文档.zip

      基于SpringBoot的校园服务系统源码数据库文档.zip

      史上最全IXIA测试仪配置使用指导手册(含IxNetwork,图文并茂超详细!).zip

      内容概要: IXIA测试仪的基本配置.doc ixia测试仪基础使用示例.doc IxNetwork如何进行抓包回放-V1.0.pdf IxNetwork如何自定义报文-V2.0.pdf ixia构造ip分片方法.txt IxNetwork使用简介.pdf 适用人群:网络协议造包、打流相关的测试工程技术人员,想要学习的同学可以下载哈 使用场景:构造pcap包,打流 Ixia简介 IXIA使用的是Server-client模式,Server端在测试仪表的主机上,在开机后会随着主机内的操作系统的启动而自动启动,一般情况下不需要人为的手工启动。因此在通常不需要为主机配置专用的显示器和键盘。 client端包括两个测试软件: Ixia Explorer和ScriptMate。这两个软件一般安装在测试用计算机上,在仪表自带的主机中也有这两个软件。根据测试项目的不同来选择使用不同的软件。Ixia Explorer主要提供数据流的测试,针对设备的功能进行测试; ScriptMate提供各种性能测试窗口,针对设备的性能进行测试。 Auto:自动分配;

      基于Python+Django花卉商城系统源码数据库文档.zip

      基于Python+Django花卉商城系统源码数据库文档.zip

      Umi-OCR-main.zip

      Umi-OCR-main.zip

      微信小程序源码-促销抽奖.zip

      基于微信小程序开发的促销抽奖小工具源码,适用于初学者了解小程序开发过程以及简单抽奖工具的实现。

      Sen2_median.txt

      GEE训练教程——Landsat5、8和Sentinel-2、DEM和各2哦想指数下载

      springboot的概要介绍与分析

      以下是一个关于Spring Boot设计的资源描述及项目源码的简要概述: Spring Boot设计资源描述 Spring Boot是一个为基于Spring的应用提供快速开发工具的框架,其设计旨在简化Spring应用的初始搭建和开发过程。以下是一些关键资源: Spring Boot官方文档:详细阐述了Spring Boot的核心特性、自动配置原理、起步依赖、内嵌式服务器等关键概念。这是学习和掌握Spring Boot设计的首选资源。 在线教程与视频:各大在线教育平台提供了丰富的Spring Boot教程和视频课程,从基础入门到高级应用,帮助开发者全面了解和掌握Spring Boot设计。 书籍与电子资料:许多技术书籍和在线电子资料深入讲解了Spring Boot的设计原理、最佳实践和项目案例,为开发者提供了宝贵的学习资源。 项目源码示例 以下是一个简单的Spring Boot项目源码示例,用于演示Spring Boot的基本结构和自动配置功能: java // 引入Spring Boot依赖 @SpringBootApplication public class MySpri

      基于springboot美妆领域管理系统源码数据库文档.zip

      基于springboot美妆领域管理系统源码数据库文档.zip

      tables-3.7.0+gpl-cp37-cp37m-win_amd64.whl

      tables-3.7.0+gpl-cp37-cp37m-win_amd64.whl

      算法实现的概要介绍与分析

      算法是计算机科学的核心,它们在解决各种问题时发挥着关键作用。一个好的算法不仅可以提高程序的效率,还可以简化复杂的问题。下面我将通过一个具体的例子——快速排序算法(Quick Sort)——来展示算法的实现过程,包括资源描述和项目源码。 ### 快速排序算法简介 快速排序是一种高效的排序算法,采用分治法的思想。其基本步骤如下: 1. 从数列中挑出一个元素,称为“基准”(pivot)。 2. 重新排序数列,所有比基准值小的元素放到基准前面,所有比基准值大的元素放到基准后面(相同的数可以到任一边)。在这个分割结束之后,该基准就处于数列的中间位置。这个称为分割(partition)操作。 3. 递归地(recursive)把小于基准值的子数列和大于基准值的子数列排序。 ### 资源描述 快速排序算法因其高效性和简洁性,在实际应用中非常受欢迎。它的时间复杂度平均为 O(n log n),最坏情况下为 O(n^2),但这种情况很少发生。快速排序的空间复杂度为 O(log n),因为它使用了递归来实现。 快速排序的一个典型应用场景是在数据库系统中对大量数据进行排序。由于它的高效性,快速排序

      基于springboot农场投入品运营线上管理系统源码数据库文档.zip

      基于springboot农场投入品运营线上管理系统源码数据库文档.zip

      基于springboot个性化影院推荐系统源码数据库文档.zip

      基于springboot个性化影院推荐系统源码数据库文档.zip

      linux基础进阶笔记

      linux基础进阶笔记,配套视频:https://www.bilibili.com/list/474327672?sid=4493093&spm_id_from=333.999.0.0&desc=1

      微信自动抢红包动态库.zip程序资源学习资料参考

      小程序 微信自动抢红包动态库.zip程序资源学习资料参考

      iOS版微信抢红包插件(支持后台抢红包).zip

      小程序 iOS版微信抢红包插件(支持后台抢红包).zip

      经典-FPGA时序约束教程

      经典-FPGA时序约束教程

      基于springboot的智慧点餐系统源码数据库文档.zip

      基于springboot的智慧点餐系统源码数据库文档.zip

    Global site tag (gtag.js) - Google Analytics