Bắt đầu với .NET Core 2.1, có một cách mới để đảo ngược chuỗi bằng string.Create
phương thức này.
Lưu ý rằng giải pháp này không xử lý chính xác các ký tự kết hợp Unicode, v.v., vì "Les Mise \ u0301rables" sẽ được chuyển đổi thành "selbarésiM seL". Các câu trả lời khác cho một giải pháp tốt hơn.
public static string Reverse(string input)
{
return string.Create<string>(input.Length, input, (chars, state) =>
{
state.AsSpan().CopyTo(chars);
chars.Reverse();
});
}
Điều này về cơ bản sao chép các ký tự của input
một chuỗi mới và đảo ngược chuỗi mới tại chỗ.
Tại sao có string.Create
ích?
Khi chúng ta tạo một chuỗi từ một mảng hiện có, một mảng bên trong mới được phân bổ và các giá trị được sao chép. Nếu không, có thể đột biến một chuỗi sau khi tạo ra nó (trong một môi trường an toàn). Đó là, trong đoạn mã sau, chúng ta phải phân bổ một mảng có độ dài 10 lần, một là bộ đệm và một là mảng bên trong của chuỗi.
var chars = new char[10];
// set array values
var str = new string(chars);
string.Create
về cơ bản cho phép chúng ta thao tác mảng bên trong trong thời gian tạo chuỗi. Đây là, chúng ta không cần một bộ đệm nữa và do đó có thể tránh phân bổ một mảng char đó.
Steve Gordon đã viết về nó chi tiết hơn ở đây . Ngoài ra còn có một bài viết về MSDN .
Sử dụng string.Create
như thế nào?
public static string Create<TState>(int length, TState state, SpanAction<char, TState> action);
Phương thức này có ba tham số:
- Độ dài của chuỗi cần tạo,
- dữ liệu bạn muốn sử dụng để tự động tạo chuỗi mới,
- và một đại biểu tạo ra chuỗi cuối cùng từ dữ liệu, trong đó tham số đầu tiên trỏ đến
char
mảng bên trong của chuỗi mới và thứ hai là dữ liệu (trạng thái) bạn truyền vào string.Create
.
Bên trong đại biểu, chúng ta có thể chỉ định cách tạo chuỗi mới từ dữ liệu. Trong trường hợp của chúng tôi, chúng tôi chỉ sao chép các ký tự của chuỗi đầu vào sang chuỗi Span
được sử dụng bởi chuỗi mới. Sau đó, chúng tôi đảo ngượcSpan
và do đó toàn bộ chuỗi được đảo ngược.
Điểm chuẩn
Để so sánh cách đề xuất của tôi để đảo ngược một chuỗi với câu trả lời được chấp nhận, tôi đã viết hai điểm chuẩn bằng cách sử dụng BenchmarkDotNet.
public class StringExtensions
{
public static string ReverseWithArray(string input)
{
var charArray = input.ToCharArray();
Array.Reverse(charArray);
return new string(charArray);
}
public static string ReverseWithStringCreate(string input)
{
return string.Create(input.Length, input, (chars, state) =>
{
state.AsSpan().CopyTo(chars);
chars.Reverse();
});
}
}
[MemoryDiagnoser]
public class StringReverseBenchmarks
{
private string input;
[Params(10, 100, 1000)]
public int InputLength { get; set; }
[GlobalSetup]
public void SetInput()
{
// Creates a random string of the given length
this.input = RandomStringGenerator.GetString(InputLength);
}
[Benchmark(Baseline = true)]
public string WithReverseArray() => StringExtensions.ReverseWithArray(input);
[Benchmark]
public string WithStringCreate() => StringExtensions.ReverseWithStringCreate(input);
}
Đây là kết quả trên máy của tôi:
| Method | InputLength | Mean | Error | StdDev | Gen 0 | Allocated |
| ---------------- | ----------- | -----------: | ---------: | --------: | -----: | --------: |
| WithReverseArray | 10 | 45.464 ns | 0.4836 ns | 0.4524 ns | 0.0610 | 96 B |
| WithStringCreate | 10 | 39.749 ns | 0.3206 ns | 0.2842 ns | 0.0305 | 48 B |
| | | | | | | |
| WithReverseArray | 100 | 175.162 ns | 2.8766 ns | 2.2458 ns | 0.2897 | 456 B |
| WithStringCreate | 100 | 125.284 ns | 2.4657 ns | 2.0590 ns | 0.1473 | 232 B |
| | | | | | | |
| WithReverseArray | 1000 | 1,523.544 ns | 9.8808 ns | 8.7591 ns | 2.5768 | 4056 B |
| WithStringCreate | 1000 | 1,078.957 ns | 10.2948 ns | 9.6298 ns | 1.2894 | 2032 B |
Như bạn có thể thấy, với ReverseWithStringCreate
chúng tôi chỉ phân bổ một nửa bộ nhớ được sử dụng bởi ReverseWithArray
phương thức.