Jaké máme možnosti? Která je nejlepší? Tyto dvě otázky jsem si nedávno položil ve chvíli, kdy jsem potřeboval napsat metodu pro výběr části textu, jejíž funkcionalita přesahuje možnosti metody text.Substring().
Mým záměrem bylo vzít prvních 200 smysluplných znaků z přijatého textu a vrátit je v podobě nového textu. Za smysluplné v tomto případě považuji všechny znaky z ASCII tabulky s hodnotou vyšší než 31. Protože i tohle je dotaz nad kolekcí dat, LINQ byla jasná volba.
static string Shorten(string text)
{
if (string.IsNullOrWhiteSpace(text)) return "";
var chars = text.Where(c => c > 31).Take(200);
return ???
}
Zaseknul jsem se ale u příkazu return. Nebylo to vyloženě, že bych nevěděl, jak dál, ale co zvolit? Na výběr je tu rovnou ze tří možností, které mě napadají.
- Jeden z konstruktorů třídy String přijímá jako parametr pole znaků – String(Char[]).
- Jedno z přetížení metody string.Concat() přijímá jako parametr sekvenci objektů libovolného typu – string.Concat<T>(IEnumerable<T>).
- Použít třídu StringBuilder.
V prvním případě by bylo nutné nejdříve sekvenci znaků převést na pole zavoláním chars.ToArray(). To na první pohled nevypadá příliš efektivně, nejdříve převést sekvenci na pole a poté to předat konstruktoru, který s ním provede svoje. Concat interně používá StringBuilder, ale předtím než každý prvek sekvence připojí, převede jej na string. Přímé použití StringBuilderu tedy zní jako nejlepší nápad. Bylo mi jasné, že nejsem první, kdo tento problém řeší a tak jsem hledal a našel otázku s řešením na stackoverflow, kde se moje teorie potvrdila testy. Nicméně, překvapilo mě, že první varianta zaostává za třetí jen nepatrně, a že Concat() je víc než 2x pomalejší oproti zbytku. Při řídkém použití by bylo úplně jedno, kterou z variant použijete, ale pokud se taková metoda nachází na tzv. “hotpath”, tedy je volána tak často, že její výkon ovlivňuje výkon celé aplikace, pak je dobré se na ni podívat opravdu zblízka.
Používám tedy pro snadnou čitelnost
static string Shorten(string text)
{
if (string.IsNullOrWhiteSpace(text)) return "";
var chars = text.Where(c => c > 31).Take(200);
return new string(chars.ToArray());
}
i když
static string Shorten(string text)
{
if (string.IsNullOrWhiteSpace(text)) return "";
var chars = text.Where(c => c > 31).Take(200);
var sb = new StringBuilder();
foreach (var c in chars)
{
sb.Append(c);
}
return sb.ToString();
}
je výkonnější. Je nutno poznamenat, že výše uvedené testy byly provedeny v izolovaném prostředí a je možné, že na jiných strojích v jiném prostředí se budou lišit. Nicméně na začátek to stačí. Kdybych viděl, že aplikace nestíhá, provedl bych vlastní měření a zařídil se podle toho. Nemá ale cenu optimalizovat předčasně. V některém z příštích článků se přímo k této metodě ještě vrátím a podíváme se na ni z trochu jiného pohledu.