字符串ctor是转换IEnumerable<;char>;到字符串

本文关键字:字符串 char gt ctor lt IEnumerable 转换 | 更新日期: 2023-09-27 17:58:36

为的发行版编辑。Net Core 2.1

为的发布重复测试。Net Core 2.1,我得到这样的结果

1000000次"迭代";Concat";耗时842毫秒。

1000000次"迭代";新字符串";耗时1009ms。

1000000次"迭代";某人;耗时902毫秒。

简而言之,如果您正在使用。Net Core 2.1或更高版本,Concat是王者。


我已经编辑了这个问题,以纳入评论中提出的有效观点。


我在思考我对之前一个问题的回答,我开始想,这是吗

return new string(charSequence.ToArray());

IEnumerable<char>转换为string的最佳方式。我搜索了一下,发现这个问题已经在这里提出了。这个答案断言,

string.Concat(charSequence)

是一个更好的选择。在回答了这个问题之后,还提出了一种StringBuilder枚举方法,

var sb = new StringBuilder();
foreach (var c in chars)
{
    sb.Append(c);
}
return sb.ToString();

虽然这可能有点笨拙,但为了完整起见,我将其包括在内。我决定做一个小测试,使用的代码在底部。

当在发布模式下构建,经过优化,并在没有附加调试器的情况下从命令行运行时,我会得到这样的结果。

1000000次"迭代";Concat";耗时1597ms。

1000000次"迭代";新字符串";耗时869ms。

1000000次"迭代";某人;耗时748毫秒。

据我估计,new string(...ToArray())方法的速度几乎是string.Concat方法的两倍。StringBuilder的速度稍快,但使用起来很尴尬,但可能是一个扩展。

我应该坚持使用new string(...ToArray())吗?还是我缺少了什么?

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
class Program
{
    private static void Main()
    {
        const int iterations = 1000000;
        const string testData = "Some reasonably small test data";
        TestFunc(
            chars => new string(chars.ToArray()),
            TrueEnumerable(testData),
            10,
            "new String");
        TestFunc(
            string.Concat,
            TrueEnumerable(testData),
            10,
            "Concat");
        TestFunc(
            chars =>
            {
                var sb = new StringBuilder();
                foreach (var c in chars)
                {
                    sb.Append(c);
                }
                return sb.ToString();
            },
            TrueEnumerable(testData),
            10,
            "sb");
        Console.WriteLine("----------------------------------------");
        TestFunc(
            string.Concat,
            TrueEnumerable(testData),
            iterations,
            "Concat");
        TestFunc(
            chars => new string(chars.ToArray()),
            TrueEnumerable(testData),
            iterations,
            "new String");
        TestFunc(
            chars =>
            {
                var sb = new StringBuilder();
                foreach (var c in chars)
                {
                    sb.Append(c);
                }
                return sb.ToString();
            },
            TrueEnumerable(testData),
            iterations,
            "sb");
        Console.ReadKey();
    }
    private static TResult TestFunc<TData, TResult>(
            Func<TData, TResult> func,
            TData testData,
            int iterations,
            string stage)
    {
        var dummyResult = default(TResult);
        var stopwatch = Stopwatch.StartNew();
        for (var i = 0; i < iterations; i++)
        {
            dummyResult = func(testData);
        }
        stopwatch.Stop();
        Console.WriteLine(
            "{0} iterations of '"{2}'" took {1}ms.",
            iterations,
            stopwatch.ElapsedMilliseconds,
            stage);
        return dummyResult;
    }
    private static IEnumerable<T> TrueEnumerable<T>(IEnumerable<T> sequence)
    {
        foreach (var t in sequence)
        {
            yield return t;
        }
    }
}

字符串ctor是转换IEnumerable<;char>;到字符串

值得注意的是,这些结果虽然从纯粹主义者的角度来看适用于IEnumerable的情况,但并不总是如此。例如,如果您实际上有一个char数组,即使您将其作为IEnumerable传递,那么调用字符串构造函数会更快。

结果:

Sending String as IEnumerable<char> 
10000 iterations of "new string" took 157ms. 
10000 iterations of "sb inline" took 150ms. 
10000 iterations of "string.Concat" took 237ms.
======================================== 
Sending char[] as IEnumerable<char> 
10000 iterations of "new string" took 10ms.
10000 iterations of "sb inline" took 168ms.
10000 iterations of "string.Concat" took 273ms.

代码:

static void Main(string[] args)
{
    TestCreation(10000, 1000);
    Console.ReadLine();
}
private static void TestCreation(int iterations, int length)
{
    char[] chars = GetChars(length).ToArray();
    string str = new string(chars);
    Console.WriteLine("Sending String as IEnumerable<char>");
    TestCreateMethod(str, iterations);
    Console.WriteLine("===========================================================");
    Console.WriteLine("Sending char[] as IEnumerable<char>");
    TestCreateMethod(chars, iterations);
    Console.ReadKey();
}
private static void TestCreateMethod(IEnumerable<char> testData, int iterations)
{
    TestFunc(chars => new string(chars.ToArray()), testData, iterations, "new string");
    TestFunc(chars =>
    {
        var sb = new StringBuilder();
        foreach (var c in chars)
        {
            sb.Append(c);
        }
        return sb.ToString();
    }, testData, iterations, "sb inline");
    TestFunc(string.Concat, testData, iterations, "string.Concat");
}

好吧,我刚刚写了一个小测试,尝试了3种不同的方法从IEnumerable创建字符串:

  1. 使用CCD_ 9并重复调用其CCD_
  2. 使用string.Concat<T>
  3. 使用CCD_ 12构造函数

10000次迭代生成一个随机的1000个字符的序列并从中构建一个字符串,我在发布构建中看到了以下时间:

  • 样式=StringBuilder经过时间为00:01:05.9687330分钟
  • Style=StringConcatFunction经过时间为00:02:33.22672485分钟
  • Style=StringConstructor经过的时间为00:04:00.55559091分钟

StringBuilder是明显的赢家。不过,我使用的是一个静态StringBuilder(singleton)实例。如果这有多大区别的话,那就不要了。

这是源代码:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
namespace ConsoleApplication6
{
  class Program
  {
    static readonly RandomNumberGenerator Random = RandomNumberGenerator.Create() ;
    static readonly byte[] buffer = {0,0} ;
    static char RandomChar()
    {
      ushort codepoint ;
      do
      {
        Random.GetBytes(buffer) ;
        codepoint = BitConverter.ToChar(buffer,0) ;
        codepoint &= 0x007F ; // restrict to Unicode C0 ;
      } while ( codepoint < 0x0020 ) ;
      return (char) codepoint ;
    }
    static IEnumerable<char> GetRandomChars( int count )
    {
      if ( count < 0 ) throw new ArgumentOutOfRangeException("count") ;
      while ( count-- >= 0 )
      {
        yield return RandomChar() ;
      }
    }
    enum Style
    {
      StringBuilder = 1 ,
      StringConcatFunction = 2 ,
      StringConstructor = 3 ,
    }
    static readonly StringBuilder sb = new StringBuilder() ;
    static string MakeString( Style style )
    {
      IEnumerable<char> chars = GetRandomChars(1000) ;
      string instance ;
      switch ( style )
      {
      case Style.StringConcatFunction :
        instance = String.Concat<char>( chars ) ;
        break ;
      case Style.StringBuilder : 
        foreach ( char ch in chars )
        {
          sb.Append(ch) ;
        }
        instance = sb.ToString() ;
        break ;
      case Style.StringConstructor :
        instance = new String( chars.ToArray() ) ;
        break ;
      default :
        throw new InvalidOperationException() ;
      }
      return instance ;
    }
    static void Main( string[] args )
    {
      Stopwatch stopwatch = new Stopwatch() ;
      foreach ( Style style in Enum.GetValues(typeof(Style)) )
      {
        stopwatch.Reset() ;
        stopwatch.Start() ;
        for ( int i = 0 ; i < 10000 ; ++i )
        {
          MakeString( Style.StringBuilder ) ;
        }
        stopwatch.Stop() ;
        Console.WriteLine( "Style={0}, elapsed time is {1}" ,
          style ,
          stopwatch.Elapsed
          ) ;
      }
      return ;
    }
  }
}