所以这是我关于StackOverflow的第一个问题,如果在这里违反任何约定,我深表歉意...
我有一个可变长度的数组,其中每个元素都是一个字符串(每个字符串元素具有相同的长度)。我需要将该数组中每个字符串的第n个字符放入它自己的数组中。
简单地说,我有以下内容:
string[] source = new string[3] { "abcd", "efgh", "ijkl" };
并想返回:
string[] output = new string[4] { "aei", "bfj", "cgk", "dhl" };
这又是非常简化的,因为实际上我正在处理MB数据,这就是为什么我需要使用LINQ而不是一堆嵌套的for循环的解决方案的原因。
提前谢谢大家!
答案 0 :(得分:0)
一项快速测试显示,将@jdweng的纯LINQ解决方案设置为1X,将LINQ滥用索引的解决方案设置为7.4X,使用for
的解决方案设置为28.4X。
(Ab)结合使用LINQ和索引:
var c = Enumerable.Range(0, source[0].Length).Select(chpos => Enumerable.Range(0, source.Length).Select(si => source[si][chpos]).Join()).ToArray();
使用嵌套的for
循环:
var sourcelen = source.Length;
var strlen = source[0].Length;
var c = new String[strlen];
var sb = new StringBuilder(strlen);
for (int chpos = 0; chpos < strlen; ++chpos) {
for (int si = 0; si < sourcelen; ++si)
sb.Append(source[si][chpos]);
c[chpos] = sb.ToString();
sb.Length = 0;
}
运行一些进一步的测试,Append
比索引StringBuilder
快一点,直到有很多长字符串并且并行(顺序)构建所有字符串都不快,并且字符串的长度增加,如果您有大量(10,000)的字符串,则Parallel.For
版本会赶上最快的嵌套for
版本,然后超过该版本。有趣的是,Append
比在Parallel
中建立索引要慢得多。
以下是每个索引并行版本的构建:
var sourcelen = source.Length;
var strlen = source[0].Length;
var c = new String[strlen];
Parallel.For(0, strlen, chpos => {
var sb = new StringBuilder(sourcelen);
sb.Length = sourcelen;
for (int si = 0; si < sourcelen; ++si)
sb[si] = source[si][chpos];
c[chpos] = sb.ToString();
});
使用这种Join
扩展方法可以大大加快使用String.Join(String.Empty,
或String.Join("",
的解决方案的速度:
public static string Join(this IEnumerable<char> src) {
var sb = new StringBuilder();
foreach (var c in src)
sb.Append(c);
return sb.ToString();
}
关于时间的一些注释:我使用LINQPad编写了一些代码来生成示例source
:
var lens = 10000;
var source = new string[10000];
//
{
var s = Enumerable.Range(0,26).Select(letternum => new String(Convert.ToChar('a'+letternum), lens)).ToArray();
for (int j1 = 0; j1 < source.Length; ++j1)
source[j1] = s[j1 % 26].ToString();
}
然后,使用LINQPad的Util.ElapsedTime
,我测量了通过各种实现方式处理source
的时间:
TimeSpan basetime;
//
{
var start = Util.ElapsedTime;
var c = source.Select(x => x.Select((y, i) => new { chr = y, index = i })).SelectMany(x => x).GroupBy(x => x.index).Select(x => x.Select(y => y.chr).Join()).ToArray();
basetime = Util.ElapsedTime - start;
basetime.Dump("Elapsed LINQ My Join 1X");
//c.Dump();
}
//
{
var start = Util.ElapsedTime;
var sourcelen = source.Length;
var strlen = source[0].Length;
var c = new String[strlen];
Parallel.For(0, strlen, chpos => {
var sb = new StringBuilder(sourcelen);
sb.Length = sourcelen;
for (int si = 0; si < sourcelen; ++si)
sb[si] = source[si][chpos];
c[chpos] = sb.ToString();
});
var myt = Util.ElapsedTime - start;
myt.Dump($"Elapsed build each indexed parallel {basetime.TotalSeconds / myt.TotalSeconds:0.0}X");
c.Dump();
}