当访问列表中的第一个元素时,最好使用myList.First()或通过索引直接访问成员(即myList [0])?
使用直接索引完成操作所需的时间更快:
static void Main(string[] args)
{
var listOfInts = new List<int>
{
1, 2, 3, 4, 5,
6, 7, 8, 9, 10
};
int? first;
var sw = new Stopwatch();
sw.Start();
first = listOfInts.First();
sw.Stop();
Console.WriteLine("First Element(.First()): [" + first + "] Took: " + sw.ElapsedTicks + " ticks");
sw.Reset();
first = null;
sw.Start();
first = listOfInts[0];
sw.Stop();
Console.WriteLine("First Element(idx[0]): [" + first + "] Took: " + sw.ElapsedTicks + " ticks");
Console.ReadLine();
}
当使用.First()
时,它需要3000-5000个刻度,而idx[0]
需要1-2个刻度。是否有使用.First()
的标准或原因?我发现大多数在线使用的例子.First()
但是,如果您知道列表中包含元素,那么为什么不通过索引直接访问它?
答案 0 :(得分:2)
你的表现测试非常糟糕。
.First()
的实际效果因素比[0]
慢约1.9倍。
以下是如何正确测试:
Func<Action, int, TimeSpan> measure = (a, c) =>
{
var sw = Stopwatch.StartNew();
for (var i = 0; i < c; i++)
{
a();
};
sw.Stop();
return sw.Elapsed;
};
var listOfInts = new List<int>
{
1, 2, 3, 4, 5,
6, 7, 8, 9, 10
};
int? value;
Action indexed = () =>
{
value = listOfInts[0];
};
Action first = () =>
{
value = listOfInts.First();
};
// warm up runs
measure(indexed, 1);
measure(first, 1);
var measurements =
Enumerable
.Range(0, 10) // run 10 separate tests
.Select(x => new
{
indexed = measure(indexed, 1000000), // 1M iterations
first = measure(first, 1000000), // 1M iterations
})
.ToArray();
Console.WriteLine(measurements.Select(x => x.indexed.TotalMilliseconds).Average());
Console.WriteLine(measurements.Select(x => x.first.TotalMilliseconds).Average());
结果如下:
10.1342 19.52894
这意味着您可以在10.1342毫秒内完成1,000,000 [0]
&amp; 19.52894毫秒1,000,000 .First()
。
实际上,它几乎没有任何区别。
答案 1 :(得分:1)
First()
也适用于其他集合类型。
如果您的接口仅定义ICollection<T>
或IEnumerable<T>
,则无法使用索引,因为它不是由这些接口定义的。要在任意位置获取元素,可以使用ElementAt(indexPosition)
。
如果您确定自己拥有T[]
或IList<T>
,请使用索引。
答案 2 :(得分:1)
First
的好处是您可以使用lambda
表达式来描述条件并获得符合该条件的第一个元素:
List<int> a = new List<int>();
int r = a.First(i => i > 5);
如果你只想要收集的第一项,那就是真正的区别。
答案 3 :(得分:1)
这取决于您的要求,并且 - 在某种程度上 - 取决于您的个人偏好。
myList[0]
:列表允许随机访问,因此使用它没有任何问题。如果你知道它是一个列表或一个数组,并且你认为第一个元素存在(或者你用ArgumentOutOfRangeException
确定),你可以像这样访问元素。此外,您的代码的任何读者都将了解您正在做的事情。myList.First()
:使用First()
,您可以通过LINQ访问第一个元素,它适用于任何IEnumerable<T>
,这为您提供了更大的灵活性。 LINQ还提供了额外的扩展方法,这些方法不仅仅是访问元素,即FirstOrDefault()
,Single()
和SingleOrDefault()
,它允许您定制对情境的访问。在缺点方面,LINQ有一些性能影响,在大多数情况下实际应该是无关紧要的。但是,如果您处于性能 重要的罕见情况之一,请注意LINQ。简而言之:如果您想要简单性和性能,请使用myList[0]
。如果您需要灵活性,请使用LINQ。虽然差异很小,所以如果你喜欢其中一种比另一种更好的方法,那就用它吧。在任何情况下,结果都将完全相同(唯一的区别是myList[0]
会引发ArgumentOutOfRangeException
,而First()
会引发InvalidOperationException
。
答案 4 :(得分:1)
我建议使用First
。为什么?有几个原因:
First
适用于任何IEnumerable
。普遍化,即使是过早的,非常便宜,通常也是一个好主意。性能影响可以忽略不计。
与某些人认为相反,First
不会创建迭代器块,然后返回第一个元素。它的作用是检查枚举是否实现IList
,如果是,它会直接返回第一个元素。有点像:
var list = source as IList<TSource>;
if (list != null)
{
if (list.Count > 0) return list[0];
}
else
{
//iterator block
}
//throw empty enumeration
答案 5 :(得分:0)
没有区别。第一个功能是使用索引器。
但如果列表中没有元素,则First()函数将抛出一个&#39; System.InvalidOperationException&#39;。但索引器会抛出System.OutOfRangeException&#39;
public static TSource First<TSource>(this IEnumerable<TSource> source) {
if (source == null) throw Error.ArgumentNull("source");
IList<TSource> list = source as IList<TSource>;
if (list != null) {
if (list.Count > 0) return list[0];
}
else {
using (IEnumerator<TSource> e = source.GetEnumerator()) {
if (e.MoveNext()) return e.Current;
}
}
throw Error.NoElements();
}