.Net框架有一个Array.Sort重载,允许指定排序操作的起始和结束索引。但是这些参数仅为32位。因此,当描述排序范围的索引只能使用64位数字指定时,我不知道如何对大数组的一部分进行排序。我想我可以复制并修改框架的排序实现,但那并不理想。
更新:
我创建了两个类来帮助我解决这些和其他大型数组问题。其中之一是,在达到内存限制之前很久,我开始收到OutOfMemoryException异常。我假设这是因为请求的内存可能可用但不连续。所以为此,我创建了BigArray类,它是一个通用的、动态可调整大小的数组列表。它比框架的通用列表类具有更小的内存占用,并且不需要整个数组是连续的。我还没有测试性能损失,但我确定会有。
这两个类可以一起使用(这就是我使用它们的方式),但不必如此。我希望其他人也能发现它们有用。但我承认,它们是边缘情况下的类。欢迎提问。如果我的代码糟糕,我也想听听建议。
最后一个想法:正如您在OrderedOperation中所看到的,我正在使用int而不是long。尽管我曾经有过原始问题(如果您无法告诉,该应用程序正在不断变化),但目前对我来说已足够。但是,该类也应该能够处理长整型,如果需要的话。
更新:
我创建了两个类来帮助我解决这些和其他大型数组问题。其中之一是,在达到内存限制之前很久,我开始收到OutOfMemoryException异常。我假设这是因为请求的内存可能可用但不连续。所以为此,我创建了BigArray类,它是一个通用的、动态可调整大小的数组列表。它比框架的通用列表类具有更小的内存占用,并且不需要整个数组是连续的。我还没有测试性能损失,但我确定会有。
public class BigArray<T> : IEnumerable<T>
{
private long capacity;
private int itemsPerBlock;
private int shift;
private List<T[]> blocks = new List<T[]>();
public BigArray(int itemsPerBlock)
{
shift = (int)Math.Ceiling(Math.Log(itemsPerBlock) / Math.Log(2));
this.itemsPerBlock = 1 << shift;
}
public long Capacity
{
get
{
return capacity;
}
set
{
var requiredBlockCount = (value - 1) / itemsPerBlock + 1;
while (blocks.Count > requiredBlockCount)
{
blocks.RemoveAt(blocks.Count - 1);
}
while (blocks.Count < requiredBlockCount)
{
blocks.Add(new T[itemsPerBlock]);
}
capacity = (long)itemsPerBlock * blocks.Count;
}
}
public T this[long index]
{
get
{
Debug.Assert(index < capacity);
var blockNumber = (int)(index >> shift);
var itemNumber = index & (itemsPerBlock - 1);
return blocks[blockNumber][itemNumber];
}
set
{
Debug.Assert(index < capacity);
var blockNumber = (int)(index >> shift);
var itemNumber = index & (itemsPerBlock - 1);
blocks[blockNumber][itemNumber] = value;
}
}
public IEnumerator<T> GetEnumerator()
{
for (long i = 0; i < capacity; i++)
{
yield return this[i];
}
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
}
回到排序的问题上来...我真正需要的是一种按顺序对数组的每个元素进行操作的方法。但是由于这些数组非常大,复制数据、排序、执行操作,然后丢弃已排序的副本(必须保持原始顺序)是不可行的。因此,我创建了一个静态类OrderedOperation,它允许您以排序的方式对未排序的数组的每个元素执行任意操作,并且具有较低的内存占用(在这里将内存换取执行时间)。
public static class OrderedOperation
{
public delegate void WorkerDelegate(int index, float progress);
public static void Process(WorkerDelegate worker, IEnumerable<int> items, int count, int maxItem, int maxChunkSize)
{
// create a histogram such that a single bin is never bigger than a chunk
int binCount = 1000;
int[] bins;
double binScale;
bool ok;
do
{
ok = true;
bins = new int[binCount];
binScale = (double)(binCount - 1) / maxItem;
int i = 0;
foreach (int item in items)
{
bins[(int)(binScale * item)]++;
if (++i == count)
{
break;
}
}
for (int b = 0; b < binCount; b++)
{
if (bins[b] > maxChunkSize)
{
ok = false;
binCount *= 2;
break;
}
}
} while (!ok);
var chunkData = new int[maxChunkSize];
var chunkIndex = new int[maxChunkSize];
var done = new System.Collections.BitArray(count);
var processed = 0;
var binsCompleted = 0;
while (binsCompleted < binCount)
{
var chunkMax = 0;
var sum = 0;
do
{
sum += bins[binsCompleted];
binsCompleted++;
} while (binsCompleted < binCount - 1 && sum + bins[binsCompleted] <= maxChunkSize);
Debug.Assert(sum <= maxChunkSize);
chunkMax = (int)Math.Ceiling((double)binsCompleted / binScale);
var chunkCount = 0;
int i = 0;
foreach (int item in items)
{
if (item < chunkMax && !done[i])
{
chunkData[chunkCount] = item;
chunkIndex[chunkCount] = i;
chunkCount++;
done[i] = true;
}
if (++i == count)
{
break;
}
}
Debug.Assert(sum == chunkCount);
Array.Sort(chunkData, chunkIndex, 0, chunkCount);
for (i = 0; i < chunkCount; i++)
{
worker(chunkIndex[i], (float)processed / count);
processed++;
}
}
Debug.Assert(processed == count);
}
}
这两个类可以一起使用(这就是我使用它们的方式),但不必如此。我希望其他人也能发现它们有用。但我承认,它们是边缘情况下的类。欢迎提问。如果我的代码糟糕,我也想听听建议。
最后一个想法:正如您在OrderedOperation中所看到的,我正在使用int而不是long。尽管我曾经有过原始问题(如果您无法告诉,该应用程序正在不断变化),但目前对我来说已足够。但是,该类也应该能够处理长整型,如果需要的话。