提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
前言
先说结论,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!