Począwszy od .NET Core 2.1 istnieje nowy sposób na odwrócenie łańcucha za pomocą string.Create
metody.
Zauważ, że to rozwiązanie nie obsługuje poprawnie łączenia znaków Unicode itp., Ponieważ „Les Mise \ u0301rables” zostałby przekonwertowany na „selbarésiM seL”. Na pozostałe odpowiedzi dla lepszego rozwiązania.
public static string Reverse(string input)
{
return string.Create<string>(input.Length, input, (chars, state) =>
{
state.AsSpan().CopyTo(chars);
chars.Reverse();
});
}
To zasadniczo kopiuje postacie input
do nowego ciągu i odwraca nowy ciąg w miejscu.
Dlaczego jest string.Create
użyteczny?
Kiedy tworzymy ciąg z istniejącej tablicy, przydzielana jest nowa tablica wewnętrzna i wartości są kopiowane. W przeciwnym razie byłoby możliwe zmutowanie łańcucha po jego utworzeniu (w bezpiecznym środowisku). Oznacza to, że w poniższym fragmencie musimy dwukrotnie przydzielić tablicę o długości 10, jedną jako bufor, a drugą jako wewnętrzną tablicę łańcucha.
var chars = new char[10];
// set array values
var str = new string(chars);
string.Create
zasadniczo pozwala nam manipulować wewnętrzną tablicą podczas tworzenia łańcucha. Oznacza to, że nie potrzebujemy już bufora i dlatego możemy uniknąć przydzielenia tej tablicy znaków.
Steve Gordon napisał o tym bardziej szczegółowo tutaj . Jest też artykuł o MSDN .
Jak korzystać string.Create
?
public static string Create<TState>(int length, TState state, SpanAction<char, TState> action);
Metoda przyjmuje trzy parametry:
- Długość ciągu do utworzenia,
- dane, których chcesz użyć do dynamicznego tworzenia nowego ciągu,
- oraz delegat, który tworzy końcowy ciąg z danych, gdzie pierwszy parametr wskazuje na wewnętrzną
char
tablicę nowego ciągu, a drugi to dane (stan), które przekazałeś string.Create
.
Wewnątrz delegata możemy określić sposób tworzenia nowego ciągu z danych. W naszym przypadku po prostu kopiujemy znaki ciągu wejściowego do Span
używanego przez nowy ciąg. Następnie odwracamySpan
a zatem cały ciąg znaków jest odwracany.
Benchmarki
Aby porównać mój proponowany sposób odwrócenia ciągu znaków z zaakceptowaną odpowiedzią, napisałem dwa testy porównawcze za pomocą 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);
}
Oto wyniki na moim komputerze:
| 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 |
Jak widać, ReverseWithStringCreate
przydzielamy tylko połowę pamięci używanej przez ReverseWithArray
metodę.