我有以下代码:
static void Main(string[] args)
{
int[] array = FillArray(); //Imagine FillArray() Fills the array with numbers from 0 to 100
string numbers = "";
for(int i = 0;i<array.Count();i++)
{
numbers += i.ToString();
}
}
我尝试做的是让这段代码更高效,也许不使用For
使用内置的C#方法,扩展方法,而不必重复此For
100次
编辑:这是一个广泛的问题,当然你会对你的知识有自己的答案我们可以说所有击败了这个算法的计时器的答案本身就足以回答这个问题。
想象一下,使用此算法迭代1.000.000.000次,然后我们可以与不同解决方案的具体数字进行比较。
答案 0 :(得分:2)
如果要多次连接字符串,请使用StringBuilder。 也可以使用array.Length。
static void Main(string[] args)
{
int[] array = FillArray(); //Imagine FillArray() Fills the array with numbers from 0 to 100
var sb = new StringBuilder();
var len = array.Length;
for(int i = 0; i < len; i++)
{
sb.Append(i.ToString());
}
var numbers = sb.ToString();
}
答案 1 :(得分:1)
尝试专为此设计的string.Concat
:
static void Main(string[] args) {
string numbers = string.Concat(FillArray());
...
}
答案 2 :(得分:1)
为了变得非常快,你需要摆脱(几乎)所有的分配。大多数人都不知道i.ToString()
分配了一个新的字符串实例,该实例在被附加到StringBuilder
实例后不再需要了。
您的初始版本确实已经结束了&#34; temp&#34;每个i.ToString()
的字符串会导致字符串逐渐变得越来越大,因此每次新的稍大的字符串时都会分配得更慢。这对于一些字符串来说不是问题,但是如果你以这种方式连接数千或数百万个字符串,看起来这个方法永远不会终止。
更好的方法是使用StringBuilder
,它基本上是一个可扩展其现有缓冲区的可变字符串。这允许有效地附加字符串而不创建新的临时字符串,因为字符串实例不可变。现在你已经非常好了,你可以通过为所有预期的整数分配足够大的char数组来做得更好。然后你不会i.ToString()
而是i.ToCharArray()
进入最终的char缓冲区。这超过了StringBuilder
方法超过2倍。
ArrayToStringSteakOverCooked 0.39s 0.40s 0.43s
ArrayToStringKeithNicolasAlternate2 0.43s 0.44s 0.46s
ArrayToStringAlois 0.17s 0.16s 0.16s
上述数字是通过此代码实现的
int[] Integers = Enumerable.Range(-5, 5 * 1000 * 1000).ToArray();
void ArrayToStringAlois()
{
char[] buffer = new char[Integers.Length * 11]; // an integer can have 10 digits plus sign = 11 digits. This buffer is big enough for all possible numbers
int startIdx = 0;
for (int i = 0; i < Integers.Length; i++)
{
startIdx += ToCharArray(Integers[i], buffer, startIdx);
}
string lret = new string(buffer, 0, startIdx);
GC.KeepAlive(lret);
}
public static int ToCharArray(int value, char[] buffer, int bufferIndex)
{
if (value == 0)
{
buffer[bufferIndex] = '0';
return 1;
}
int len = 1;
int upperBound = 0;
if (value < 0)
{
buffer[bufferIndex] = '-';
len = 2;
upperBound = 1;
}
value = Math.Abs(value);
for (int rem = value / 10; rem > 0; rem /= 10)
{
len++;
}
for (int i = len - 1; i >= upperBound; i--)
{
buffer[bufferIndex + i] = (char)('0' + (value % 10));
value /= 10;
}
return len;
}
void ArrayToStringSteakOverCooked()
{
var numbers = new StringBuilder();
var length = Integers.Length;
for (int i = 0; i < length; i++)
{
numbers.Append(i.ToString());
}
var lret = numbers.ToString();
GC.KeepAlive(lret);
}
void ArrayToStringKeithNicolasAlternate2()
{
var lret = string.Concat(Integers);
GC.KeepAlive(lret);
}
答案 3 :(得分:0)
有多种不同的方法可以获得更好的性能。在我的测试中,StringBuilder可能有轻微的优势(时间在多次运行中是可变的,数字非常接近,超过10次迭代,StringBuilder始终是最快的,其他两次保持交换速度更快)
static void Orginal(int[] array)
{
string numbers = "";
for (int i = 0; i < array.Count(); i++)
{
numbers += i.ToString();
}
}
static void Improved(int[] array)
{
var numbers = new StringBuilder();
for (int i = 0; i < array.Length; i++)
{
numbers.Append(i);
}
}
static void Alternate(int[] array)
{
var strings = array.Select(n => n.ToString()).ToArray();
var s = string.Join("", strings);
}
static void Alternate2(int[] array)
{
string.Concat(array);
}
static void Time(string name, Action<int[]> action, int[] array)
{
Stopwatch stopwatch = Stopwatch.StartNew();
action(array);
stopwatch.Stop();
Console.WriteLine("{0} - {1}", name, stopwatch.ElapsedMilliseconds);
}
static void Main(string[] args)
{
var array = Enumerable.Range(0, 100000).ToArray();
for (int i = 0; i < 10; i++)
{
Time("Original", Orginal, array);
Time("Improved", Improved, array);
Time("Alternate", Alternate, array);
Time("Alternate2", Alternate2, array);
}
}