Skip to main content

字符串-常考面试题

1. StringBuilder和String的区别?

在C#中,String 和 StringBuilder 是用于处理字符串的两种主要类,但它们在内存管理、性能以及用途上有显著的区别。

String

  1. String 是不可变的(immutable),这意味着一旦创建了一个 String 对象,就不能更改它的值。任何对字符串的修改操作(如连接、替换、删除等)实际上都会创建一个新的 String 对象,并将原始对象保留在内存中,直到垃圾回收器(Garbage Collector)清理它。
  2. String 类提供了许多用于操作字符串的方法,如 Substring(), Replace(), IndexOf(), LastIndexOf(), ToUpper(), ToLower(), Trim() 等。
  3. 由于 String 的不可变性,当进行大量字符串操作时(特别是连接操作),可能会导致性能下降和内存浪费。

总结

字符串本身是不可变的,在此基础上操作原来的字符串,底层机制会产生原字符串副本然后在副本上进行修改,并不会修改原来的字符串。所以每当程序修改一次字符,原字符串会遗留下原来的字符串,长此以往高频使用,产生额外的内存开销,内存利用率将会下降,影响程序性能。

StringBuilder

  1. StringBuilder 是可变的(mutable),它提供了一个可变长的缓冲区,可以在其中对文本进行修改,而不会像 String 那样创建新的对象。这使得 StringBuilder 在进行大量字符串操作时更加高效。
  2. StringBuilder 类也提供了许多用于操作字符串的方法,如 Append(), AppendFormat(), Insert(), Remove(), Replace() 等。
  3. StringBuilder 维护一个内部缓冲区,并根据需要自动调整大小。当使用 Append() 或类似方法添加文本时,它会在内部缓冲区中分配更多空间(如果需要的话),并将新文本添加到现有文本的末尾。这避免了创建大量新的字符串对象所需的开销。
  4. 当你完成了所有的字符串修改操作后,可以使用 ToString() 方法将 StringBuilder 对象转换为一个 String 对象。此时,StringBuilder 内部缓冲区的内容将被复制到一个新的 String 对象中,并且 StringBuilder 可以被安全地释放或重用。

代码举例

下面是一个简单的例子,展示了使用 String 和 StringBuilder 进行字符串连接的性能差异:

using System;  
using System.Diagnostics;
using System.Text;

class Program {
static void Main() {
const int loopCount = 10000;
Stopwatch stopwatch = new Stopwatch();

// 使用 String 进行连接
stopwatch.Start();
string resultString = "";
for (int i = 0; i < loopCount; i++) {
resultString += "text";
}
stopwatch.Stop();
Console.WriteLine($"String concatenation time: {stopwatch.ElapsedMilliseconds} ms");

// 使用 StringBuilder 进行连接
stopwatch.Restart();
StringBuilder resultStringBuilder = new StringBuilder();
for (int i = 0; i < loopCount; i++) {
resultStringBuilder.Append("text");
}
string resultFromStringBuilder = resultStringBuilder.ToString();
stopwatch.Stop();
Console.WriteLine($"StringBuilder concatenation time: {stopwatch.ElapsedMilliseconds} ms");
}
}

在这个例子中,当循环次数较大时(如10000次),使用 StringBuilder 进行字符串连接的性能会明显优于使用 String。这是因为每次使用 += 运算符连接字符串时,都会创建一个新的 String 对象,并将原始对象保留在内存中。而 StringBuilder 则在内部缓冲区中管理文本,避免了这种开销。

2. C# String类型比 StringBuilder 类型的优势是什么

在C#中,String和StringBuilder类型都用于处理字符串,但它们有各自的优势和适用场景。String类型是不可变的(immutable),而StringBuilder类型是可变的(mutable),这使得它们在处理字符串时表现出不同的行为。

String类型的优势

  1. 简单性:String类型是语言内置的,使用起来非常简单直观。对于简单的字符串操作,如赋值、比较和连接少量字符串,String类型通常足够使用。
  2. 线程安全性:由于String类型是不可变的,它在多线程环境中是安全的。多个线程可以同时读取同一个String对象,而不需要额外的同步措施。
  3. 内存管理:虽然String类型的不可变性可能会导致在大量字符串操作时产生额外的内存开销,但对于小量的字符串操作,垃圾回收器可以有效地管理这些临时对象。

StringBuilder类型的优势

  1. 性能:StringBuilder类型在处理大量字符串连接或修改操作时性能更好。由于它是可变的,它可以在内部缓冲区中直接修改字符串,而不是创建新的字符串对象。这减少了内存分配和垃圾回收的开销。
  2. 灵活性:StringBuilder提供了更多用于字符串操作的方法,如Append、Insert、Remove和Replace等,使得字符串的修改更加灵活。

代码举例:

String类型示例:

string str1 = "Hello";  
string str2 = "World";
string result = str1 + " " + str2; // 连接字符串
Console.WriteLine(result); // 输出:Hello World

在这个例子中,我们使用String类型连接了两个字符串,并输出了结果。虽然对于这样的小量字符串操作,性能差异可能不明显,但如果是大量字符串的连续连接,性能问题就会凸显出来。

StringBuilder类型示例:

StringBuilder sb = new StringBuilder();  
sb.Append("Hello");
sb.Append(" ");
sb.Append("World");
string result = sb.ToString(); // 转换为字符串
Console.WriteLine(result); // 输出:Hello World

在这个例子中,我们使用StringBuilder类型来构建字符串。通过调用Append方法,我们将多个部分添加到StringBuilder对象中,最后通过调用ToString方法将其转换为String类型。这种方式在处理大量字符串连接时更加高效。

总结

String类型和StringBuilder类型各有优势,选择使用哪一种取决于具体的应用场景。String主要用于公共 API,通用性好、用途广泛、读取性能高、占用内存小。String是不可变的,所以天然线程同步。StringBuilder可变,非线程同步。对于简单的字符串操作或少量字符串的连接,String类型通常是足够的,对于需要频繁修改或连接大量字符串的情况,使用StringBuilder类型可以显著提高性能。不过现在的编译器已经把String的 + 操作优化成 StringBuilder 了, 所以一般用String 就可以了

private string[] ExtractFileNames(string content)
{
// 这里需要根据你服务器返回的内容进行适当的解析
// 这个例子仅用于演示,并假设服务器以HTML形式返回文件列表
// 你可能需要使用正则表达式、HTML解析库或其他方法来提取文件名
// 这里是一个简单的例子,假设文件名位于href属性中
List<string> fileNames = new List<string>();
string pattern = "href=\"([^\"]+)\"";
MatchCollection matches = Regex.Matches(content, pattern);

foreach (Match match in matches)
{
string fileName = match.Groups[1].Value;
fileNames.Add(fileName);
}

return fileNames.ToArray();
}