分割数组的最佳方法

下午,我需要找出将arrays拆分成较小“块”的最佳方法。

我正在传递大约1200个项目,并且需要将这些项目拆分为更容易处理的100个组,然后我需要将它们传递给处理。

有人可以提出一些建议吗?

您可以使用LINQ按块大小对所有项目进行分组,然后创建新的数组。

 // build sample data with 1200 Strings string[] items = Enumerable.Range(1, 1200).Select(i => "Item" + i).ToArray(); // split on groups with each 100 items String[][] chunks = items .Select((s, i) => new { Value = s, Index = i }) .GroupBy(x => x.Index / 100) .Select(grp => grp.Select(x => x.Value).ToArray()) .ToArray(); for (int i = 0; i < chunks.Length; i++) { foreach (var item in chunks[i]) Console.WriteLine("chunk:{0} {1}", i, item); } 

请注意,不必创建新数组(需要cpu周期和内存)。 当您省略两个ToArrays时,您还可以使用IEnumerable>

这是运行代码: http : //ideone.com/K7Hn2

Array.Copy自1.1以来一直存在,并且在块数组方面做得非常出色。

 string[] buffer; for(int i = 0; i < source.Length; i+=100) { buffer = new string[100]; Array.Copy(source, i, buffer, 0, 100); // process array } 

并为它做一个扩展:

 public static class Extensions { public static T[] Slice(this T[] source, int index, int length) { T[] slice = new T[length]; Array.Copy(source, index, slice, 0, length); return slice; } } 

并使用扩展名:

 string[] source = new string[] { 1200 items here }; // get the first 100 string[] slice = source.Slice(0, 100); 

更新:我认为你可能想要ArraySegment<>不需要性能检查,因为它只是使用原始数组作为源,并维护一个Offset和Count属性来确定'segment'。 不幸的是,没有办法将段作为数组检索JUST,所以有些人为它编写了包装器,就像这里: ArraySegment - 返回实际的段C#

 ArraySegment segment; for (int i = 0; i < source.Length; i += 100) { segment = new ArraySegment(source, i, 100); // and to loop through the segment for (int s = segment.Offset; s < segment.Array.Length; s++) { Console.WriteLine(segment.Array[s]); } } 

Array.Copy与Skip / Take vs LINQ的性能

测试方法(在发布模式下):

 static void Main(string[] args) { string[] source = new string[1000000]; for (int i = 0; i < source.Length; i++) { source[i] = "string " + i.ToString(); } string[] buffer; Console.WriteLine("Starting stop watch"); Stopwatch sw = new Stopwatch(); for (int n = 0; n < 5; n++) { sw.Reset(); sw.Start(); for (int i = 0; i < source.Length; i += 100) { buffer = new string[100]; Array.Copy(source, i, buffer, 0, 100); } sw.Stop(); Console.WriteLine("Array.Copy: " + sw.ElapsedMilliseconds.ToString()); sw.Reset(); sw.Start(); for (int i = 0; i < source.Length; i += 100) { buffer = new string[100]; buffer = source.Skip(i).Take(100).ToArray(); } sw.Stop(); Console.WriteLine("Skip/Take: " + sw.ElapsedMilliseconds.ToString()); sw.Reset(); sw.Start(); String[][] chunks = source .Select((s, i) => new { Value = s, Index = i }) .GroupBy(x => x.Index / 100) .Select(grp => grp.Select(x => x.Value).ToArray()) .ToArray(); sw.Stop(); Console.WriteLine("LINQ: " + sw.ElapsedMilliseconds.ToString()); } Console.ReadLine(); } 

结果(以毫秒为单位):

 Array.Copy: 15 Skip/Take: 42464 LINQ: 881 Array.Copy: 21 Skip/Take: 42284 LINQ: 585 Array.Copy: 11 Skip/Take: 43223 LINQ: 760 Array.Copy: 9 Skip/Take: 42842 LINQ: 525 Array.Copy: 24 Skip/Take: 43134 LINQ: 638 

你可以使用Skip()Take()

 string[] items = new string[]{ "a", "b", "c"}; string[] chunk = items.Skip(1).Take(1).ToArray(); 
  string[] amzProductAsins = GetProductAsin();; List chunks = new List(); for (int i = 0; i < amzProductAsins.Count; i += 100) { chunks.Add(amzProductAsins.Skip(i).Take(100).ToArray()); } 

在这里我发现了另一个linq解决方案:

 int[] source = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; int i = 0; int chunkSize = 3; int[][] result = source.GroupBy(s => i++ / chunkSize).Select(g => g.ToArray()).ToArray(); //result = [1,2,3][4,5,6][7,8,9] 

您可以使用List.GetRange :

 for(var i = 0; i < source.Count; i += chunkSize) { List items = source.GetRange(i, Math.Min(chunkSize, source.Count - i)); } 

虽然不像Array.Copy那样快,但我觉得它看起来更干净:

 var list = Enumerable.Range(0, 723748).ToList(); var stopwatch = new Stopwatch(); for (int n = 0; n < 5; n++) { stopwatch.Reset(); stopwatch.Start(); for(int i = 0; i < list.Count; i += 100) { List c = list.GetRange(i, Math.Min(100, list.Count - i)); } stopwatch.Stop(); Console.WriteLine("List.GetRange: " + stopwatch.ElapsedMilliseconds.ToString()); stopwatch.Reset(); stopwatch.Start(); for (int i = 0; i < list.Count; i += 100) { List c = list.Skip(i).Take(100).ToList(); } stopwatch.Stop(); Console.WriteLine("Skip/Take: " + stopwatch.ElapsedMilliseconds.ToString()); stopwatch.Reset(); stopwatch.Start(); var test = list.ToArray(); for (int i = 0; i < list.Count; i += 100) { int length = Math.Min(100, list.Count - i); int[] c = new int[length]; Array.Copy(test, i, c, 0, length); } stopwatch.Stop(); Console.WriteLine("Array.Copy: " + stopwatch.ElapsedMilliseconds.ToString()); stopwatch.Reset(); stopwatch.Start(); List> chunks = list .Select((s, i) => new { Value = s, Index = i }) .GroupBy(x => x.Index / 100) .Select(grp => grp.Select(x => x.Value).ToList()) .ToList(); stopwatch.Stop(); Console.WriteLine("LINQ: " + stopwatch.ElapsedMilliseconds.ToString()); } 

结果以毫秒为单位:

 List.GetRange: 1 Skip/Take: 9820 Array.Copy: 1 LINQ: 161 List.GetRange: 9 Skip/Take: 9237 Array.Copy: 1 LINQ: 148 List.GetRange: 5 Skip/Take: 9470 Array.Copy: 1 LINQ: 186 List.GetRange: 0 Skip/Take: 9498 Array.Copy: 1 LINQ: 110 List.GetRange: 8 Skip/Take: 9717 Array.Copy: 1 LINQ: 148 

使用LINQ,可以使用Take()和Skip()函数

一般递归扩展方法:

  public static IEnumerable> SplitList(this IEnumerable source, int maxPerList) { var enumerable = source as IList ?? source.ToList(); if (!enumerable.Any()) { return new List>(); } return (new List>() { enumerable.Take(maxPerList) }).Concat(enumerable.Skip(maxPerList).SplitList(maxPerList)); } 

如果你有一个要分割的数组,但是除了这个简单的解决方案,你可以平等地分享各个“块”中缺少的元素。 在我的解决方案中,块被链接为一个长字符串,但您可以轻松地更改它。

  public static string[] SplitArrey(string[] ArrInput, int n_column) { string[] OutPut = new string[n_column]; int NItem = ArrInput.Length; // total elements int ItemsForColum = NItem / n_column; // n elements for each chunk int _total = ItemsForColum * n_column; // Count the equal elements int MissElement = NItem - _total; // Count missing element int[] _Arr = new int[n_column]; for (int i = 0; i < n_column; i++) { int AddOne = (i < MissElement) ? 1 : 0; _Arr[i] = ItemsForColum + AddOne; } int offset = 0; for (int Row = 0; Row < n_column; Row++) { for (int i = 0; i < _Arr[Row]; i++) { OutPut[Row] += ArrInput[i + offset] + " "; // <- Here to change how the strings are linked } offset += _Arr[Row]; } return OutPut; }