【学习笔记】Contains和indexof性能比较

本文探讨了在C#中,对于字符串和列表数据结构,`Contains`与`IndexOf`方法的性能差异。实验结果显示,对于字符串,`Contains`方法效率更高,而对于列表,两者性能接近。源码分析表明,`Contains`主要寻找是否存在,而`IndexOf`进行精确匹配,涉及更多比较操作。在大数据量时,列表中使用`IndexOf`可能更优,但差异不大,优化时才需考虑转换。
摘要由CSDN通过智能技术生成

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

先说结论,String类型时,Contains效率高;List是Contain和Indexof效率近似,但Index会比Contain好一点。

使用工具:VS2019,.NET5.0,ILSpy


提示:以下测试数据

一、数据测试

说明:建立四个方法,分别调用即可。

1.1设计思路

建立string和List类型,然后用contain和indexof分别测试。

1.2代码演示

//测试Contain 和 indexof的效率问题
    class OneTest
    {
        private string TestString = "qazwsxedcrfvtgbyhnujmikolp  Searches a section of the list for a given element using a binary searchalgorithm.Elements of the list are compared to the search value usingthe given IComparer interface. If comparer is null, elements ofthe list are compared to the search value using the IComparableinterface, which in that case must be implemented by all elements of thelist and the given search value.This method assumes that the givensection of the list is already sorted; if this is not the case, theresult will be incorrect.The method returns the index of the given value in the list. If thelist does not contain the given value, the method returns a negativeinteger. The bitwise complement operator (~) can be applied to anegative result to produce the index of the first element (if any) thatis larger than the given search value. This is also the index at whichthe search value should be inserted into the list in order for the listto remain sorted.";
        private List<string> TestStrings = new List<string>() { "Searches", "a", "section", "of", "the", "list", "for", "a", "given", "element", "using", "a", "binary", "searchalgorithm.Elements", "of", "the", "list", "are", "compared", "to", "the", "search", "value", "usingthe", "given", "IComparer", "interface.", "If", "comparer", "is", "null,", "elements", "ofthe", "list", "are", "compared", "to", "the", "search", "value", "using", "the", "IComparableinterface,", "which", "in", "that", "case", "must", "be", "implemented", "by", "all", "elements", "of", "thelist", "and", "the", "given", "search", "value.This", "method", "assumes", "that", "the", "givensection", "of", "the", "list", "is", "already", "sorted;", "if", "this", "is", "not", "the", "case,", "theresult", "will", "be", "incorrect.The", "method", "returns", "the", "index", "of", "the", "given", "value", "in", "the", "list.", "If", "thelist", "does", "not", "contain", "the", "given", "value,", "the", "method", "returns", "a", "negativeinteger.", "The", "bitwise", "complement", "operator", "(~)", "can", "be", "applied", "to", "anegative", "result", "to", "produce", "the", "index", "of", "the", "first", "element", "(if", "any)", "thatis", "larger", "than", "the", "given", "search", "value.", "This", "is", "also", "the", "index", "at", "whichthe", "search", "value", "should", "be", "inserted", "into", "the", "list", "in", "order", "for", "the", "listto", "remain", "sorted." };

        public OneTest()
        {
        }

        //用时:29.2014ms
        public void TestStringByContain()
        {
            Stopwatch watch = new Stopwatch();
            watch.Start();
            for (int i = 0; i < 1000000; i++)
            {
                var a = TestString.Contains("given");
            }

            watch.Stop();
            TimeSpan timespan = watch.Elapsed;

            Console.WriteLine("数据100万条,TestString ByContain全部完成...用时:" + timespan.TotalMilliseconds + "ms");
        }

        //用时:534.4299ms
        public void TestStringByindexof()
        {
            Stopwatch watch = new Stopwatch();
            watch.Start();
            for (int i = 0; i < 1000000; i++)
            {
                var a = TestString.IndexOf("given");
            }

            watch.Stop();
            TimeSpan timespan = watch.Elapsed;

            Console.WriteLine("数据100万条,TestString Byindexof 全部完成...用时:" + timespan.TotalMilliseconds + "ms");
        }
        
        //用时:
        public void TestStringsByContain()
        {
            Stopwatch watch = new Stopwatch();
            watch.Start();
            for (int i = 0; i < 1000000; i++)
            {
                var a = TestStrings.Contains("given");
            }

            watch.Stop();
            TimeSpan timespan = watch.Elapsed;

            Console.WriteLine("数据100万条,Contain全部完成...用时:" + timespan.TotalMilliseconds + "ms");
        }

        public void TestStringsByindexof()
        {
            Stopwatch watch = new Stopwatch();
            watch.Start();
            for (int i = 0; i < 1000000; i++)
            {
                var a = TestStrings.IndexOf("given");
            }

            watch.Stop();
            TimeSpan timespan = watch.Elapsed;

            Console.WriteLine("数据100万条,Contain全部完成...用时:" + timespan.TotalMilliseconds + "ms");
        }
    }

1.3实验结果

1.TestStringByContain,用时:29.2014ms
2.TestStringByindexof,用时:534.4299ms
3.TestStringsByContain,用时:81.8809ms
4.TestStringsByindexof,用时:71.6459ms

二、源码分析

2.1 string中Contains方法解析

2.1.1 源码阅读

第一层调用
1.初步代码分析:
若传入值为空,则返回【异常】
否则,返回bool,若大于等于0为true;小于0为false

2.进入SpanHelpers.IndexOf(ref _firstChar, Length, ref value._firstChar, value.Length)方法

在这里插入图片描述
在这里插入图片描述
我对【重要方法和重要变量】两个地方进行了标注:
比较重要的3个代码:
Unsafe.Add【添加】
IndexOf【获取首字母相同的字符】
SequenceEqual【判断序列是否相等】

重要变量:
【第一个Num2 +第二个Num2起的效果】和上一句综合看,检测数字(num2)一定要大于0;实际上是在说,被搜索字符一定要大于搜索字符;
【之后的Num2】均起缩小检测范围的作用
Num3:定位首字符出现的位置【确定的】
Num4:定位首字符出现的位置【每次检测都会变】

2.1.2 推测:

举例:在string “吃葡萄不吐葡萄皮”中用Contain查找是否包含string“葡萄”
我觉得,Contains方法的编写原理是
1.判断被检测字符【吃葡萄不吐葡萄皮】长度要大于检测字符长度【葡萄】
2.在string 被检测字符中找到char类型的【葡】,若全文无葡,则返回-1
【Indexof来找】
3.找到第一个葡字,然后查词是否为【葡萄】,是,返回字符;不是,进行下一次检测。【SequenceEqual方法,进行比较】

2.2 string中indexof方法解析

2.2.1 源码阅读

在这里插入图片描述
他本质上是调用了,另外一个Indexof方法,这个也是为啥,我们写IndexOf的时候指定【比较格式】效率会高一点的原因

下面是第二层调用:
在这里插入图片描述
第三层调用:
在这里插入图片描述
进函数看看:
在这里插入图片描述
分成三部分、
第一部分是,判断两个数都不能是空,
第二部分是,判断检测数长度不能大于被检测数,
第三部分调用了另外一个Index函数
在这里插入图片描述
分成了两部分
用了if和switch、if里是满足前置条件后递进,Switch则是并列的选择。
得到一个数字。

推论:
我之后没再点进去仔细看里面的代码了,但是里面确实依旧还嵌套了很多层indexOf。

第二部分总结:
由此可以得知,indexof主要是在判断大小写和语言上耗费了过多的性能。
且,string类型中 contain调用的index方法和string中index方法并不一样,这个也是需要额外注意的。

2.3 List中方法解析

前言:根据运行时间猜测,我们会认为Contain和Index 会比较接近。

2.3.1 Contains方法

在这里插入图片描述
调用了Index(item)方法,
在这里插入图片描述

2.3.2 Index方法

在这里插入图片描述

2.3.3 List中两方法小结

本质上,List方法中,是List的contains调用的是List的Indexof。故而,在性能比较中,List中Contain较Index会稍慢一些。


总结

通过比较2.1和2.2中代码,我们可以得出,string命名空间中,Contain与Index方法的底层实现完全不同。
Contain着重在于找到string就结束
Index重点在于精准的比较字符,用了大量的代码来实现精准比较。

string类型,不要求精准,contain即可;要求精准,用indexof,且指定比较方式会节省一点,但不会太多。
List类型,大数据量的时用index比较好,但其实也没差很多,我认为只有优化的时候,可以考虑contain转化成index。

最后

感谢我的挚友查尔斯,是他提出了这个有趣的问题和现象。让我解锁了ILSpy这个新技能。
Thank you for much!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值