使用C#的同学对List应该并不陌生,我们不需要初始化它的大小,并且可以方便的使用Add和Remove方法执行添加和删除操作,但却可以使用下标来访问它的数据,它是我们常说的链表吗?

     List<int> ls = new List<int>();
    ls.Add(1);
    Console.WriteLine(ls[0]); //输出 1

先简单回顾一下链表的概念。

什么是链表

链表是一种线性表数据结构,在内存中它可以是非连续的,通过在每个结点中使用指针存储下一个结点的地址来实现逻辑顺序。一个结点由两部分组成:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域

链表由很多种类,常见的有:单链表、双链表和循环链表,它们实现的原理差别不大,相对于单链表只是多添加了一些特定的功能,所以今天主要讲解最简单、最常用的单链表。

单链表添加、删除结点

由于链表是通过指针来指向下一个结点,所以添加和删除操作需要改变指针的指向即可。并且它们的时间复杂度都是O(1).

单链表查找指定结点

数组可以通过下标和寻址公式使用O(1)的时间复杂度来访问指定结点,但是由于链表结点在内存中可以是非连续的,无法通过寻址公式计算对应的内存地址,所以要查找一个结点就只能依次遍历,时间复杂度为O(n).

C#中的List

既然链表不能通过下标访问,那上面例子中ls[0]为什么会输出1呢?

查看源码,首先从它的Add方法开始,在vs中点击f12进入,发现跳转到List类内部的SynchronizedList类中,Add函数定义如下

    public void Add(T item)
  {
       lock (_root)
      {
           _list.Add(item);
      }
  }

目前还没有看出什么问题,继续查看 _list.Add方法

    public void Add(T item)
  {
       if (_size == _items.Length)
      {
           //确保不超出容量,否则会执行扩容操作
           EnsureCapacity(_size + 1);
      }

       _items[_size++] = item;
       _version++;
  }

_items[_size++] = item这句看起来是不是很眼熟,不就是向数组中添加一个元素嘛。为了严谨,我们再查看_items是什么

    private const int _defaultCapacity = 4;
private T[] _items;
private int _size;
private int _version;

果不其然,_items就是一个泛型数组,并且还有_size_version等其他字段

原来List其实并不是链表,在内存中它也是使用数组来进行存储的,只是对添加、删除等操作进行了封装,并且使其能够“自动扩容”。

LinkedList链表

这才是C#中真正的链表,不过它不是最简单的单链表,而是一个双链表。单链表只有一个指针结点指向它的下一个元素,而双链表中每个结点有两个指针结点,一个指向它的下一个元素,另一个指向它的上一个元素。

下面是LinkedListNode的部分源码,可以看到它包含一个next指针和一个prev指针。

    internal LinkedList<T> list;
internal LinkedListNode<T> next;
internal LinkedListNode<T> prev;

具体怎么使用,这里就不具体讲解了,看官方文档也比较容易理解。

链表和数组的区别

ok,现在我们对数组和链表都有了一定程度上的了解,那能不能归纳出它们之间有哪些区别呢?对数组有不清楚的地方,可以查看文章为什么数组从0开始编号

  1. 从逻辑结构上看,它们都是属于线性表这个数据结构

  2. 从内存上来看,数组是顺序存储,占用一块连续的内存,大小固定,扩容成本大;链表是链式存储,也就是非连续的,并且因为它多了一个指针,所以不存在大小限制,天然支持动态扩容,但占用的内存也更大。

  3. 访问操作:数组可以支持“随机访问”,O(1)的时间复杂度;链表需要按顺序逐个访问,O(n)的时间复杂度.

  4. 添加和删除操作:数组的时间复杂度为O(n); 链表的时间复杂度为O(1)

  5. 操作系统内存管理方面,借助CPU的缓存机制,可以预读数组的内容,所以访问效率更高,而链表则不行。

总结

C#中List是链表吗?为什么可以通过下标访问的更多相关文章

  1. 如何在程序中给word文档加上标和下标

    如何在程序中给word文档加上标和下标 上标或下标是一个小于普通行格式的数字,图形,标志或者指示通常它的设置与行相比偏上或偏下.下标通常显示于或者低于基准线,而上标则高于.上标和下标通常被用于表达公式 ...

  2. C++中public,protected,private派生类继承问题和访问权限问题

    C++中public,protected,private派生类继承问题和访问权限问题 当一个子类从父类继承时,父类的所有成员成为子类的成员,此时对父类成员的访问状态由继承时使用的继承限定符决定. 1. ...

  3. Swift Tips - 在 Swift 中自定义下标访问

    Untitled Document.md input[type="date"].form-control,.input-group-sm>input[type="d ...

  4. java中对集合对象list的几种循环访问

    java中对集合对象list的几种循环访问的总结如下 1 经典的for循环 public static void main(String[] args) { List<String> li ...

  5. SQL Server 2005中设置Reporting Services发布web报表的匿名访问

    原文:SQL Server 2005中设置Reporting Services发布web报表的匿名访问 一位朋友提出个问题:集成到SQL Server 2005中的Reporting Services ...

  6. (转)java中对集合对象list的几种循环访问总结

    Java集合的Stack.Queue.Map的遍历   在集合操作中,常常离不开对集合的遍历,对集合遍历一般来说一个foreach就搞定了,但是,对于Stack.Queue.Map类型的遍历,还是有一 ...

  7. Java中public,protected,default,private的访问权限问题(简明扼要)

    import packa.*;//导入了packa包中所有的类.(不包括包中的子包)一般不会用,用哪个导入哪个. 导包的原则:用到哪个类,就导入哪个类.所有字母都小写. 权限列表:   public ...

  8. Web.config中加了system.diagnostics节点后就不能访问了

    Web.config中加了system.diagnostics节点后就不能访问了,怎么回事? [解决方法] 不要把system.diagnostics节点作为web.config的第一个节点.

  9. 在IIS中部署好WCF服务站点后,本机访问服务无问题,局域网中其他电脑访问不到

    1.问题描述 在IIS中部署好WCF服务站点后,本机访问服务无问题,局域网中其他电脑访问不到. 2.解决方法 (1)控制面板 -> Windows防火墙 -> 高级设置 (2)属性 (3) ...

随机推荐

  1. 日志导致jvm内存溢出相关问题

    生产环境日志级别为info,请看如下这行代码: LOGGER.debug("the DTO info: {}", JSON.toJSONString(DTO)); 这段代码主要有两 ...

  2. 终极蛇皮上帝视角之铁头娃之鲁迅之暑假闲的慌之bilibili看尚学堂网课的非洲酋长java小复习

    转自https://www.sxt.cn/Java_jQuery_in_action/eight-cache-problem.html 第一个点 自动装箱与拆箱的功能是所谓的"编译器蜜糖(C ...

  3. 使用Postfix与Dovecot收发电子邮件(物理机虚拟机之间)

    邮件应用协议包括: 简单邮件传输协议(SMTP),用来发送或中转发出的电子邮件,占用tcp 25端口. 第三版邮局协议(POP3),用于将服务器上把邮件存储到本地主机,占用tcp 110端口. 第四版 ...

  4. 008 PCI设备BAR空间的初始化

    一.PCI设备BAR空间的初始化 在PCI Agent设备进行数据传送之前,系统软件需要初始化PCI Agent设备的BAR0~5寄存器和PCI桥的Base.Limit寄存器.系统软件使用DFS算法对 ...

  5. 如何保证前端项目上线后的安全?webfunny已总结前端最关键的12大指标

    实时监控大屏   众所周知:实时流量大屏,是用来监控前端项目上线质量的. 如大家所知,监控系统会监控线上应用的各项指标,如:错误.白屏.耗时等等,但是仔细一想,即使有这些监控,我们也不一定能够保证线上 ...

  6. 剑指 Offer 68 - I. 二叉搜索树的最近公共祖先

    剑指 Offer 68 - I. 二叉搜索树的最近公共祖先 给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先. 百度百科中最近公共祖先的定义为:"对于有根树 T 的两个结点 p.q ...

  7. java操作excel 工具类

    java操作excel 可参考https://blog.csdn.net/xunwei0303/article/details/53213130 直接上代码: 一.java生成excel文件: pac ...

  8. 【转】Mysql中事务ACID实现原理

    转自:https://www.cnblogs.com/rjzheng/p/10841031.html 作者:孤独烟 引言 照例,我们先来一个场景~ 面试官:"知道事务的四大特性么?" ...

  9. 关于Junit中Assert已经过时

    在junit4.12之后,Assert就过时了,提供了TestCase来取代: 同样在TestCase中原本比较常见的一些方法也已经取消了,例如:assertNotEquals.assertThat. ...

  10. 使用filter过滤GZIP压缩(二)

    在代码之前,讲一下用filter实现GZIP压缩的原理: 因为GZIP压缩之后,是从服务器端传输到浏览器端,从servlet到浏览器(从jsp到浏览器),其实是response带回内容,所以我们要在f ...