Лучший способ конвертировать IEnumerable в string?
почему нельзя использовать беглый язык на string
?
например:
var x = "asdf1234";
var y = new string(x.TakeWhile(char.IsLetter).ToArray());
нет ли лучшего способа конвертировать IEnumerable<char>
до string
?
вот тест, который я сделал:
class Program
{
static string input = "asdf1234";
static void Main()
{
Console.WriteLine("1000 times:");
RunTest(1000, input);
Console.WriteLine("10000 times:");
RunTest(10000,input);
Console.WriteLine("100000 times:");
RunTest(100000, input);
Console.WriteLine("100000 times:");
RunTest(100000, "ffff57467");
Console.ReadKey();
}
static void RunTest( int times, string input)
{
Stopwatch sw = new Stopwatch();
sw.Start();
for (int i = 0; i < times; i++)
{
string output = new string(input.TakeWhile(char.IsLetter).ToArray());
}
sw.Stop();
var first = sw.ElapsedTicks;
sw.Restart();
for (int i = 0; i < times; i++)
{
string output = Regex.Match(input, @"^[A-Z]+",
RegexOptions.IgnoreCase).Value;
}
sw.Stop();
var second = sw.ElapsedTicks;
var regex = new Regex(@"^[A-Z]+",
RegexOptions.IgnoreCase);
sw.Restart();
for (int i = 0; i < times; i++)
{
var output = regex.Match(input).Value;
}
sw.Stop();
var third = sw.ElapsedTicks;
double percent = (first + second + third) / 100;
double p1 = ( first / percent)/ 100;
double p2 = (second / percent )/100;
double p3 = (third / percent )/100;
Console.WriteLine("TakeWhile took {0} ({1:P2}).,", first, p1);
Console.WriteLine("Regex took {0}, ({1:P2})." , second,p2);
Console.WriteLine("Preinstantiated Regex took {0}, ({1:P2}).", third,p3);
Console.WriteLine();
}
}
результат:
1000 times:
TakeWhile took 11217 (62.32%).,
Regex took 5044, (28.02%).
Preinstantiated Regex took 1741, (9.67%).
10000 times:
TakeWhile took 9210 (14.78%).,
Regex took 32461, (52.10%).
Preinstantiated Regex took 20669, (33.18%).
100000 times:
TakeWhile took 74945 (13.10%).,
Regex took 324520, (56.70%).
Preinstantiated Regex took 172913, (30.21%).
100000 times:
TakeWhile took 74511 (13.77%).,
Regex took 297760, (55.03%).
Preinstantiated Regex took 168911, (31.22%).
заключение: я сомневаюсь, что лучше предпочесть, я думаю, что я собираюсь пойти на TakeWhile
что медленнее только при первом запуске.
в любом случае, мой вопрос в том, есть ли способ оптимизировать производительность путем рестринга результата
6 ответов
предполагая, что вы ищете преимущественно производительность, то что-то вроде этого должно быть значительно быстрее, чем любой из ваших примеров:
string x = "asdf1234";
string y = x.LeadingLettersOnly();
// ...
public static class StringExtensions
{
public static string LeadingLettersOnly(this string source)
{
if (source == null)
throw new ArgumentNullException("source");
if (source.Length == 0)
return source;
char[] buffer = new char[source.Length];
int bufferIndex = 0;
for (int sourceIndex = 0; sourceIndex < source.Length; sourceIndex++)
{
char c = source[sourceIndex];
if (!char.IsLetter(c))
break;
buffer[bufferIndex++] = c;
}
return new string(buffer, 0, bufferIndex);
}
}
как насчет этого, чтобы преобразовать IEnumerable<char>
to string
:
string.Concat(x.TakeWhile(char.IsLetter));
отредактировано для выпуска .Net Core 2.1
повторяя тест для выпуска .Net Core 2.1, я получаю такие результаты, как это
1000000 итераций "Concat" заняло 842ms.
1000000 итераций "новой строки"заняли 1009ms.
1000000 итераций " sb " заняло 902 МС.
короче говоря, если вы используете .Net Core 2.1 или более поздней версии,Concat
- король.
посмотреть MS блог для более подробной информации.
Я сделал это предметом еще вопрос
я провел тестирование производительности 3 простых методов преобразования новая строка функция concat StringBuilder в моем тестировании это подробно описано в связан вопрос, for 1000000 итераций "конкатенацию" взял 1597ms. 1000000 итераций "новой строки"заняло 869ms. 1000000 итераций "StringBuilder" взял 748ms. это говорит мне о том, что нет веских причин использовать Я бы предостерег свое утверждение, на практике все эти методы работают нормально, и все это может быть над оптимизацией.IEnumerable<char>
до string
, эти методы
return new string(charSequence.ToArray());
return string.Concat(charSequence)
var sb = new StringBuilder();
foreach (var c in charSequence)
{
sb.Append(c);
}
return sb.ToString();
1000000
варианты "Some reasonably small test data"
Я получаю такие результаты,
string.Concat
для этой задачи. Если вы хотите простоту, используйте новая строка подход и если хотите производительности используйте StringBuilder.
почему нельзя использовать беглый язык в строке?
это возможно. Вы сделали это в самом вопросе:
var y = new string(x.TakeWhile(char.IsLetter).ToArray());
нет ли лучшего способа конвертировать
IEnumerable<char>
в строку?
(мое предположение:)
платформа не имеет такого конструктора, потому что строки неизменяемы, и вам придется дважды пересечь перечисление, чтобы предварительно выделить память для строки. Этот это не всегда вариант, особенно если входной поток.
единственное решение этого-нажать на резервный массив или StringBuilder
во-первых, и перераспределить по мере роста ввода. Для чего-то такого низкого уровня, как строка, это, вероятно, следует считать слишком скрытым механизмом. Это также подтолкнет проблемы perf вниз в класс string, поощряя людей использовать механизм, который не может быть как-быстро-как-можно.
эти проблемы легко решаются требование к пользователю использовать ToArray
метод расширения.
как указывали другие, вы можете достичь того, чего хотите (perf и выразительный код), если вы пишете код поддержки и обертываете этот код поддержки в метод расширения, чтобы получить чистый интерфейс.
вы можете очень часто делать лучшую производительность. Но что это тебе даст? Если это действительно горлышко бутылки для вашего приложения, и вы измерили его, я бы придерживался Linq TakeWhile()
версия: это наиболее читаемое и поддерживаемое решение, и это то, что считается для большинства приложений.
Если вы действительно ищут необработанную производительность, которую вы можете выполнить преобразование вручную - следующее было вокруг фактора 4+ (в зависимости от ввода длина строки) быстрее, чем TakeWhile()
в моих тестах-но я бы не использовал его лично, если бы это не было критично:
int j = 0;
for (; j < input.Length; j++)
{
if (!char.IsLetter(input[j]))
break;
}
string output = input.Substring(0, j);