我使用的语言是c#
。
让我们想要遍历名为customers
的序列的元素,这是一个名为Customer
的虚构类型的对象序列。在代码方面,我们有以下内容:
IEnumerable<Customer> customers = module.GetCustomers();
其中module
是服务层的类,通过其中一种方法,我们可以检索所有客户。通过customers
的元素进行迭代将是:
foreach(var customer in customers)
{
}
现在让我们想要在迭代customers
的元素之后获得客户数量。这可以像下面这样做:
int numberOfCustomers = customers.Count();
我现在的疑虑/问题如下:
使用Count()
方法,我们再次遍历customers
的元素。但是,如果我们已经创建了此对象的内存集合,则调用方法ToList()
:
List<Customer> customers = module.GetCustomers()
.ToList();
我们将使用列表O(1)
的{{1}}属性获得Count
中的客户数量。
为了找出这两个选项之间的最佳选择,我编写了一个简单的控制台应用程序,并使用customers
类对它们进行了分析。但是,我没有得到明确的结果。
这两个选项中哪一个最好?
更新
我运行了以下控制台应用程序:
StopWatch
然后我跑了这个:
class Program
{
static void Main(string[] args)
{
IEnumerable<int> numbers = Enumerable.Range(0, 1000);
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
foreach (var number in numbers)
Console.WriteLine(number);
Console.WriteLine(numbers.Count());
stopwatch.Stop();
// I got 175ms
Console.WriteLine(stopwatch.ElapsedMilliseconds);
Console.ReadKey();
stopwatch.Restart();
List<int> numbers2 = numbers.ToList();
foreach (var number in numbers2)
Console.WriteLine(number);
Console.WriteLine(numbers2.Count);
stopwatch.Stop();
// I got 86ms
Console.WriteLine(stopwatch.ElapsedMilliseconds);
Console.ReadKey();
}
}
答案 0 :(得分:2)
我通常更喜欢让我的存储库方法返回IReadOnlyCollection<>
,这有助于调用者知道他们可以安全地多次迭代它:
IReadOnlyCollection<Customer> customers = module.GetCustomers();
如果我无法做到这一点,并且我知道我将重复我多次给出的内容,我通常会使用.ToList()来确保我正在处理内存中的集合:
var customers = module.GetCustomers().ToList();
如果客户已经是内存中的集合,那么通过创建列表会增加一些开销,但它有助于避免通过执行诸如从数据库中检索数据等操作来创建大量开销的风险次。
由于某些原因,您的基准测试存在缺陷,但其中一个最大的原因是它使用Console.WriteLine()
执行I / O操作。该操作将花费很长时间,而不是迭代集合并计算结果。实际上,Console.WriteLine()
中花费的时间方差将超过您正在测试的代码中的差异。
但这实际上说明了我的观点 - I / O操作比CPU和内存操作花费的时间长得多,因此添加.ToList()
通常是值得的,这可能会增加运行时间的微秒,为了避免添加I / O操作的最轻微的可能性,这可以增加毫秒。