一、string字符串
上次我们提到了string是一个引用类型,今天我们就来深入了解一下string字符串的用法。
首先就是字符串的定义(双引号)string str = "涽川厂";
字符串之间相加或者字符串和数值类型相加都相当于拼接,且字符串只能存在“+”,不能存在其他运算str += "是个gay";
我们上次也讲到字符串类似字符数组,我们可以使用索引器去取其中的字符。我们在对字符串进行操作的时候,并不是操作字符串本身,而是只要有一次操作,就会创建一个新的字符串。
在String类中有很多字符串方法,我们挑选了几个常用的讲(重载方法相见https://docs.microsoft.com/zh-cn/dotnet/api/system.string?view=netframework-4.6.1)。
我们首先定义两个字符串
string test1 = "Hello";
string test2 = "World";
Compare
比较字符串的内容,判断字符串某些字符是否相等。结果为-1代表不相等,1代表两个字符串相等。
int result1 = String.Compare(test1, test2);
Console.WriteLine(result1);
Contains
返回一个bool值,在字符串中寻找我们提供的字串是否存在。
bool result2 = test1.Contains("ll");
Console.WriteLine(result2);
Remove
返回一个新字符串,它相当于从当前字符串删除了指定数量的字符。
string result3 = test1.Remove(2, 1);
Console.WriteLine(result3);
Concat
把多个字符串实例合并为一个实例
string result4 = String.Concat(test1, test2);
Console.WriteLine(result4);
CopyTo
把从选定的下标开始的特定数量的字符复制到数组的一个全新实例中
char[] destination = { 'T', 'h', 'e', ' ', 'i', 'n', 'i', 't' };
test1.CopyTo(2, destination, 3, 2);
Console.WriteLine(destination);
Format
格式化包含各种值的字符串和如何格式化每个值的说明符
int age = 17;
string str1 = String.Format("涽川厂今年{0}岁了", age);
Console.WriteLine(str1);
IndexOf
定位字符串中第一次出现某个给定子字符串或字符的位置
int result5 = test1.IndexOf("l");
Console.WriteLine(result5);
IndexOfAny
定位字符串中第一次出现某个字符或一组字符的位置
char[] c = { 'l', 'e', 'a', 'w' };
int result6 = test1.IndexOfAny(c);
Console.WriteLine(result6);
Insert
把一个字符串实例插入到另一个字符串实例的指定索引处
string result7 = test1.Insert(0, test2);
Console.WriteLine(result7);
Join
合并字符串数组,创建一个新的字符串
string[] str2 = { "abc", "def", "ghi" };
string result8 = String.Join("-", str2);
Console.WriteLine(result8);
LastIndexOf
与IndexOf一样,但定位最后一次出现的位置
int result9 = test1.LastIndexOf("l");
Console.WriteLine(result9);
LastIndexOfAny
与IndexOfAny一样,但定位最后一次出现的位置
int result10 = test1.LastIndexOfAny(c);
Console.WriteLine(result10);
PadLeft
在字符串的左侧,通过添加指定的重复字符填充字符串
char pad = '.';
string result11 = test1.PadLeft(10, pad);
Console.WriteLine(result11);
PadRight
早字符串的右侧,通过添加指定的重复字符填充字符串
string result12 = test1.PadRight(10, pad);
Console.WriteLine(result12);
Replace
用另一个字符或子字符串替换字符串中给定的字符或子字符串
string result13 = test1.Replace('l', 'r');
Console.WriteLine(result13);
Split
在出现给定字符的地方,把字符串拆分为一个子字符串数组
string[] result14 = test1.Split('e');
foreach (var item in result14)
{
Console.WriteLine(item);
}
Substring
在字符串中检索给定位置的子字符串
string result15 = test1.Substring(0);
Console.WriteLine(result15);
ToLower
把字符串转换为小写形式
string result16 = test1.ToLower();
Console.WriteLine(result16);
ToUpper
把字符串转换为大写形式
string result17 = test1.ToUpper();
Console.WriteLine(result17);
Trim
删除首尾的空白
string result18 = test1.Trim('o');
Console.WriteLine(result18);
String类是个功能非常强大的类,它实现许多有用的方法。但是,String类存在一个问题:重复修改给定的字符串,效率会很低,它实际是一个不可变的数据类型,一旦对字符串对象进行了初始化,该字符串就不能改变了。表面上修改字符串内容的方法和运算符实际上创建一个新的字符串。如:
string greetingText = "Hello, World!";
greetingText += "Hello, Shanghai!";
在执行这段代码的时候,.NET运行库会为该字符串分配足够的内存空间来保存这个文本,再设置变量greetingText,来表示这个字符串实例。
从语法上看,下一行代码是把更多的文本添加到字符串中。实际上并非如此,而是创建一个新字符串实例,给他足够的内存,以存储合并的文本。
二、StringBuilder类
从上方第三题我们会发现,Replace()方法以一种智能的方式工作,在某种程度上,它并没有去创建一个新的字符串,除非它实际上要对旧字符串进行某些改变,原来的字符串中包含多个字母,所以在调用一次Replace()方法就会去分配一个新字符串,因此在执行的过程中需要在堆上创建多个存储字符的字符串对象,最终等待被垃圾收集!显然,如果使用字符串频繁进行文字处理,应用程序会遇到严重的性能问题。为了解决这类问题,系统提供了System.Textr.StringBuilder类。
在使用String类构造一个字符串时,要给它分配足够的内存来保存字符串,然而,StringBuilder类通常分配的内存会比它需要的多。我们可以指定StringBuilder要分配多少内存,如果没有指定,那么默认情况下会根据它实例的字符串长度来确定内存的大小。 StringBuilder有两个主要的属性:
- Length 指定字符串的实际长度
- Capacity 指定字符串在分配的内存中的最大长度
对字符串的修改就在赋予StringBuilder实例的内存中进行,这就大大提高了追加子字符串和替换单个字符的效率。
StringBuilder主要有3种创建方式
//第一种创建方式 只提供一个字符串
StringBuilder str1 = new StringBuilder("Hello World!");
//第二种创建方式 提供一个指定的容量
StringBuilder str2 = new StringBuilder(20);
//第三种创建方式 提供一个指定的容量 并且有一个最大容量(可增长)
StringBuilder str3 = new StringBuilder(20, 100);
Console.WriteLine(str1.Length);
Console.WriteLine(str1.Capacity);
StringBuilder也有很多方法我就不再一一做介绍了。
方法 | 作用 |
---|---|
Append | 给当前字符串追加一个字符串 |
AppendFormat | 追加特定格式的字符串 |
Insert | 在当前字符串中插入一个子字符串 |
Remove | 从当前字符串中删除字符 |
Replace | 在当前字符串中,用某个字符(串)替换另一个字符(串) |
ToString | 返回强制转换为System.String对象的字符串 |
三、重载
方法重载是指在同一个类中方法同名,参数不同,调用时根据实
参的形式,选择与他匹配的方法执行操作的一种技术。
这里所说的参数不同是指以下几种情况:
1、参数的类型不同
2、参数的个数不同
3、参数的个数相同时他们的先后顺序不同
重载和返回值没有任何关系,可以是任何的返回值
参数类型不同、个数不同,都能构成重载
public void Print(int a)
{
Console.WriteLine(a);
}
public void Print(string str)
{
Console.WriteLine(str);
}
public void Print(int a, int b)
{
Console.WriteLine(a + b);
}
public int Print(int a, int b, int c)
{
return a + b + c;
}
四、递归
递归的本质就是自己调用自己
递归本身就是一个循环
递归的思想就是无限地接近已知的数
递归必须要有一个开始条件和一个结束条件
递归一般用来解决三类问题:
- 数据的定义是按递归定义的。(Fibonacci函数,n的阶乘)
- 问题解法按递归实现。(回溯)
- 数据的结构形式是按递归定义的。(二叉树的遍历,图的搜索)
递归也有一个致命缺点,递归解题相对常用的算法如普通循环等,运行效率较低。因此,应该尽量避免使用递归,除非没有更好的算法或者某种特定情况,递归更为适合的时候。
这里以Fibonacci数列为例:
public static int Fibonacci(int n)
{
if (n == 1) //如果TA的第一项为1的话直接返回
return 1;
else if (n == 2) //如果第二项为1的话直接返回
return 1;
else
return Fibonacci(n - 1) + Fibonacci(n - 2);
}