出于好奇,是否有更快/更有效的方法来解析int
的动态string
列表?
目前我有这个,它的工作非常好;我只是觉得可能有更好的方法,因为对于这么简单的事情来说这似乎有点过于复杂。
public static void Send(string providerIDList)
{
String[] providerIDArray = providerIDList.Split('|');
var providerIDs = new List<int>();
for (int counter = 0; counter < providerIDArray.Count(); counter++)
{
providerIDs.Add(int.Parse(providerIDArray[counter].ToString()));
}
//do some stuff with the parsed list of int
编辑:也许我应该说一个更简单的方法从字符串中解析出我的列表。但由于最初的问题确实表明更快,更有效,所选择的答案将反映出这一点。
答案 0 :(得分:14)
肯定有更好的方法。使用LINQ:
var providerIDs = providerIDList.Split('|')
.Select(x => int.Parse(x))
.ToList();
或者使用方法组转换而不是lambda表达式:
var providerIDs = providerIDList.Split('|')
.Select(int.Parse)
.ToList();
这不是最有效的方式,但它可能是最简单的。它与您的方法一样高效 - 尽管可以相当轻松地提高效率,例如给List
初始容量。
性能上的差异可能是无关紧要的,所以我会坚持使用这个简单的代码,直到你得到它是瓶颈的证据。
请注意,如果您不需要List<int>
- 如果您只需要迭代一次 - 您可以终止ToList
来电并使用providerIDs
作为{{ 1}}。
编辑:如果我们从事效率业务,那么这里是IEnumerable<int>
方法的改编版,以避免使用ForEachChar
:
int.Parse
注意:
public static List<int> ForEachCharManualParse(string s, char delim)
{
List<int> result = new List<int>();
int tmp = 0;
foreach(char x in s)
{
if(x == delim)
{
result.Add(tmp);
tmp = 0;
}
else if (x >= '0' && x <= '9')
{
tmp = tmp * 10 + x - '0';
}
else
{
throw new ArgumentException("Invalid input: " + s);
}
}
result.Add(tmp);
return result;
}
语句代替switch
可以进一步提高效果(提高约10-15%)如果这些都不是问题,那么它比我机器上的x >= '0' && x <= '9'
快约7倍:
ForEachChar
可以解决这些限制,但我没有打扰......如果他们对您有重大顾虑,请告诉我。
答案 1 :(得分:2)
到目前为止,我不喜欢任何答案。因此,为了实际回答OP提出的“最快/最有效”的String.Split与Int.Parse的问题,我编写并测试了一些代码。
在Intel 3770k上使用Mono。
我发现使用String.Split + IEnum.Select并不是最快(也许是最漂亮的)解决方案。事实上它是最慢的。
以下是一些基准测试结果
ListSize 1000 : StringLen 10468
SplitForEach1000 Time : 00:00:02.8704048
SplitSelect1000 Time : 00:00:02.9134658
ForEachChar1000 Time : 00:00:01.8254438
SplitParallelSelectr1000 Time : 00:00:07.5421146
ForParallelForEachChar1000 Time : 00:00:05.3534218
ListSize 100000 : StringLen 1048233
SplitForEach100000 Time : 00:00:01.9500846
SplitSelect100000 Time : 00:00:02.2662606
ForEachChar100000 Time : 00:00:01.2554577
SplitParallelSelectr100000 Time : 00:00:02.6509969
ForParallelForEachChar100000 Time : 00:00:01.5842131
ListSize 10000000 : StringLen 104824707
SplitForEach10000000 Time : 00:00:18.2658261
SplitSelect10000000 Time : 00:00:20.6043874
ForEachChar10000000 Time : 00:00:10.0555613
SplitParallelSelectr10000000 Time : 00:00:18.1908017
ForParallelForEachChar10000000 Time : 00:00:08.6756213
以下是获取基准测试结果的代码
using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Diagnostics;
namespace FastStringSplit
{
class MainClass
{
public static void Main (string[] args)
{
Random rnd = new Random();
char delim = ':';
int[] sizes = new int[]{1000, 100000, 10000000 };
int[] iters = new int[]{10000, 100, 10};
Stopwatch sw;
List<int> list, result = new List<int>();
string str;
for(int s=0; s<sizes.Length; s++) {
list = new List<int>(sizes[s]);
for(int i=0; i<sizes[s]; i++)
list.Add (rnd.Next());
str = string.Join(":", list);
Console.WriteLine(string.Format("\nListSize {0} : StringLen {1}", sizes[s], str.Length));
////
sw = new Stopwatch();
for(int i=0; i<iters[s]; i++) {
sw.Start();
result = SplitForEach(str, delim);
sw.Stop();
}
Console.WriteLine("SplitForEach" + result.Count + " Time : " + sw.Elapsed.ToString());
////
sw = new Stopwatch();
for(int i=0; i<iters[s]; i++) {
sw.Start();
result = SplitSelect(str, delim);
sw.Stop();
}
Console.WriteLine("SplitSelect" + result.Count + " Time : " + sw.Elapsed.ToString());
////
sw = new Stopwatch();
for(int i=0; i<iters[s]; i++) {
sw.Start();
result = ForEachChar(str, delim);
sw.Stop();
}
Console.WriteLine("ForEachChar" + result.Count + " Time : " + sw.Elapsed.ToString());
////
sw = new Stopwatch();
for(int i=0; i<iters[s]; i++) {
sw.Start();
result = SplitParallelSelect(str, delim);
sw.Stop();
}
Console.WriteLine("SplitParallelSelectr" + result.Count + " Time : " + sw.Elapsed.ToString());
////
sw = new Stopwatch();
for(int i=0; i<iters[s]; i++) {
sw.Start();
result = ForParallelForEachChar(str, delim);
sw.Stop();
}
Console.WriteLine("ForParallelForEachChar" + result.Count + " Time : " + sw.Elapsed.ToString());
}
}
public static List<int> SplitForEach(string s, char delim) {
List<int> result = new List<int>();
foreach(string x in s.Split(delim))
result.Add(int.Parse (x));
return result;
}
public static List<int> SplitSelect(string s, char delim) {
return s.Split(delim)
.Select(int.Parse)
.ToList();
}
public static List<int> ForEachChar(string s, char delim) {
List<int> result = new List<int>();
int start = 0;
int end = 0;
foreach(char x in s) {
if(x == delim || end == s.Length - 1) {
if(end == s.Length - 1)
end++;
result.Add(int.Parse (s.Substring(start, end-start)));
start = end + 1;
}
end++;
}
return result;
}
public static List<int> SplitParallelSelect(string s, char delim) {
return s.Split(delim)
.AsParallel()
.Select(int.Parse)
.ToList();
}
public static int NumOfThreads = Environment.ProcessorCount > 2 ? Environment.ProcessorCount : 2;
public static List<int> ForParallelForEachChar(string s, char delim) {
int chunkSize = (s.Length / NumOfThreads) + 1;
ConcurrentBag<int> result = new ConcurrentBag<int>();
int[] chunks = new int[NumOfThreads+1];
Task[] tasks = new Task[NumOfThreads];
for(int x=0; x<NumOfThreads; x++) {
int next = chunks[x] + chunkSize;
while(next < s.Length) {
if(s[next] == delim)
break;
next++;
}
//Console.WriteLine(next);
chunks[x+1] = Math.Min(next, s.Length);
tasks[x] = Task.Factory.StartNew((o) => {
int chunkId = (int)o;
int start = chunks[chunkId];
int end = chunks[chunkId + 1];
if(start >= s.Length)
return;
if(s[start] == delim)
start++;
//Console.WriteLine(string.Format("{0} {1}", start, end));
for(int i = start; i<end; i++) {
if(s[i] == delim || i == end-1) {
if(i == end-1)
i++;
result.Add(int.Parse (s.Substring(start, i-start)));
start = i + 1;
}
}
}, x);
}
Task.WaitAll(tasks);
return result.ToList();
}
}
}
以下是我推荐的功能
public static List<int> ForEachChar(string s, char delim) {
List<int> result = new List<int>();
int start = 0;
int end = 0;
foreach(char x in s) {
if(x == delim || end == s.Length - 1) {
if(end == s.Length - 1)
end++;
result.Add(int.Parse (s.Substring(start, end-start)));
start = end + 1;
}
end++;
}
return result;
}
为什么它更快?
它不会先将字符串拆分为数组。它同时进行拆分和解析,因此没有额外的开销来迭代字符串以拆分它然后迭代数组来解析它。
我还使用任务投入了并行化版本,但在非常大的字符串的情况下它只会更快。
答案 2 :(得分:0)
这看起来更干净:
var providerIDs = providerIDList.Split('|').Select(x => int.Parse(x)).ToList();
答案 3 :(得分:-1)
如果你真的想知道最有效的方法,那么使用不安全的代码,从字符串定义char指针,迭代所有字符递增字符指针,缓冲读取字符直到下一个'|',将缓冲字符转换为int32。如果你想要真的很快,那么手动做(从最后一个char开始,子结构值'0'char,将它乘以10,100,1000 ...根据迭代变量,然后将它添加到sum变量。我不有时间编写代码,但希望你能得到这个想法