myList.First()vs myList [0] - 哪个更好?

时间:2017-06-30 06:57:10

标签: c# list

当访问列表中的第一个元素时,最好使用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()但是,如果您知道列表中包含元素,那么为什么不通过索引直接访问它?

6 个答案:

答案 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。为什么?有几个原因:

  1. 泛化,First适用于任何IEnumerable。普遍化,即使是过早的,非常便宜,通常也是一个好主意。
  2. 更具可读性;代码更易于阅读和理解。
  3. 性能影响可以忽略不计。

    与某些人认为相反,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();
}