猿问

如何在不使用额外内存的情况下连接列表?

你如何在不加倍内存的情况下连接巨大的列表?


考虑以下代码段:


 Console.WriteLine($"Initial memory size: {Process.GetCurrentProcess().WorkingSet64 /1024 /1024} MB");

 int[] a = Enumerable.Range(0, 1000 * 1024 * 1024 / 4).ToArray();

 int[] b = Enumerable.Range(0, 1000 * 1024 * 1024 / 4).ToArray();

 Console.WriteLine($"Memory size after lists initialization: {Process.GetCurrentProcess().WorkingSet64 / 1024 / 1024} MB");

 List<int> concat = new List<int>();

 concat.AddRange(a.Skip(500 * 1024 * 1024 / 4));

 concat.AddRange(b.Skip(500 * 1024 * 1024 / 4));

 Console.WriteLine($"Memory size after lists concatenation: {Process.GetCurrentProcess().WorkingSet64 / 1024 / 1024} MB");

输出是:


Initial memory size: 12 MB

Memory size after lists initialization: 2014 MB

Memory size after lists concatenation: 4039 MB

我想在连接后将内存使用量保持在 2014 MB,而不修改 a 和 b。


回首忆惘然
浏览 96回答 3
3回答

拉风的咖菲猫

如果你需要一个List<int>,你不能这样做。AList<int>总是直接包含它的数据,所以当你有两个(比如说)100 个元素的数组和一个通过连接这两个元素创建的列表时,你已经有了 400 个独立元素。你无法改变这一点。您正在寻找的是一种不创建数据的独立副本的方法。如果您只是在搜索它(就像评论中的声音一样),您可以使用使用IEnumerable<int>LINQ 创建的:IEnumerable<int>&nbsp;concat&nbsp;=&nbsp;a.Concat(b);如果您需要类似 anIReadOnlyList<T>甚至 an 的东西IList<T>,您可以自己实现这些接口以在多个数组上创建适配器 - 但您可能需要自己编写。如果你能坚持IEnumerable<T>使用 LINQ,使用 LINQ 会简单很多。

慕仙森

我可以建议您进行一些优化:IEnumerable<int>在不调用 ToArray() 方法的情况下初始化 a 和 bint size = 1000 * 1024 * 1024 / 4;IEnumerable<int> a = Enumerable.Range(0, size);IEnumerable<int> b = Enumerable.Range(0, size);用已知容量初始化 concatList<int> concat = new List<int>(size);结果我得到以下输出:Initial memory size: 12 MBMemory size after lists initialization: 13 MBMemory size after lists concatenation: 1021 MB如果您只想串联搜索某些内容,则可以这样做而无需额外分配:IEnumerable<int> concat = a.Skip(500 * 1024 * 1024 / 4).Concat(b.Skip(500 * 1024 * 1024 / 4));int search = concat.Count(i => i % 2 == 0);Console.WriteLine($"Search result: {search}");

人到中年有点甜

他们是执着的。我只需要连接它们,进行一些搜索,然后处理连接列表如果您只需要进行一些搜索,为什么首先需要连接?分别搜索两个数组。您正在搜索的内容可能会桥接两个数组。如果是这种情况,为了让事情变得更容易并且不支付内存价格,只需实现一个模拟操作但实际上不执行它的包装器:sealed class Concatenated<T>:&nbsp; &nbsp; IReadOnlyList<T>{&nbsp; &nbsp; public static Concatenated<T>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; Concatenate<T>(&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; IReadOnlyList<T> first,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; IReadOnlyList<T> second)&nbsp; &nbsp; &nbsp; &nbsp; => new ConcatenatedArray<T>(first, second);&nbsp; &nbsp; private readonly IReadOnlyList<T>&nbsp; &nbsp; &nbsp; &nbsp;first, second;&nbsp; &nbsp; private Concatenated(&nbsp; &nbsp; &nbsp; &nbsp; IReadOnlyList<T> first,&nbsp; &nbsp; &nbsp; &nbsp; IReadOnlyList<T> second)&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; this.first = first;&nbsp; &nbsp; &nbsp; &nbsp; this.second = second;&nbsp; &nbsp; }&nbsp; &nbsp; public T this[int index]&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; => index < first.Length ?&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;first[index]:&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;second[index - first.Length];&nbsp; &nbsp; public int Count => first.Length + second.Length;&nbsp; &nbsp; public IEnumerator<T> GetEnumerator()&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; foreach (var f in first)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; yield return f;&nbsp; &nbsp; &nbsp; &nbsp; foreach (var s in second)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; yield return s;&nbsp; &nbsp; }&nbsp; &nbsp; IEnumerator IEnumerable.GetEnumerator()&nbsp; &nbsp; &nbsp; &nbsp; => GetEnumerator();}
随时随地看视频慕课网APP
我要回答