【问题标题】:When using Take(...) on a List<T>, is the entire list returned before Take(...) is applied?在 List<T> 上使用 Take(...) 时,是否在应用 Take(...) 之前返回整个列表?
【发布时间】:2016-02-09 14:23:40
【问题描述】:

我有一个List 类型的元素

    public class FriendList 
    {
        public List<string> friends { get; set; } // List of friends names
        public DateTime timestamp { get; set; } // date/time on the data file
    }

我需要一个程序来获取由timestamp 排序的前 2 个(然后用它们做一些其他的事情)。所以我开始写的是

    public void CompareLastTwo ( )
    {
        if ( this._fhist.Count < 2 )
        {
            Console.WriteLine("Need at least two instances of Facebook profile data in the Data folder");
        }

        FriendList latest, secondLatest; 
        if ( this._fhist[0].timestamp > this._fhist[1].timestamp )
        {
            latest = this._fhist[0];
            secondLatest = this._fhist[1];
        }
        else
        {
            latest = this._fhist[1];
            secondLatest = this._fhist[0];
        }

        for ( int i = 2, n = this._fhist.Count; i < n; ++i )
        {
            if ( this._fhist[i].timestamp > latest.timestamp )
            {
                secondLatest = latest;
                latest = this._fhist[i];
            }
            else if ( this._fhist[i].timestamp > secondLatest.timestamp && this._fhist[i].timestamp <= latest.timestamp )
            {
                secondLatest = this._fhist[i];
            }
        }

        // ... 

    }

但后来我通过查看How to get first N elements of a list in C#? 意识到我可以做到

List<FriendList> latestTwoFriendLists = this._fhist.OrderBy(L => L.timestamp).Take(2);

哪个更紧凑,但 是否同样高效????或者等式右边的计算过程是否在Takeing first 2之前得到一个完整的有序列表?

【问题讨论】:

  • 好吧,从逻辑上讲,如果您希望前两项成为“最低”两项,则必须先进行排序
  • @Rob 不,你可以像我一样通过集合一次迭代找到最低的两个
  • 嗯,是的,但是您仍在迭代整个集合,OrderBy 也是这样做的。 OrderBy.Take 是延迟执行的,所以虽然 OrderBy 确实会迭代整个集合,但它不会创建新的有序列表。
  • @Rob 我会假设 OrderBy 对集合进行快速排序,这意味着它不仅仅是对集合进行一次迭代
  • @Rob 由OrderBy 完成的常规排序总是 O(n * log(n)),选择“top-k”(其中 k 是固定的,不取决于 n)是 O (n) - 见 en.wikipedia.org/wiki/Category:Selection_algorithms - 像 QuickSelect。

标签: c# .net linq optimization


【解决方案1】:

OrderBy 将在Take 请求第一项时对整个集合进行排序。

所以整个 LINQ 查询将是 O(n*log(n)),而不是像您现有的代码那样 O(n)

【讨论】:

  • 是的,但在这种情况下,最好使用更紧凑和更明显的代码,直到它成为瓶颈。
  • 它在返回迭代器之前不对集合进行排序。它只在返回迭代器之前对参数进行空检查。当请求第一项时,它确实需要对整个集合进行排序。这是一个重要的区别。
【解决方案2】:

一般来说,OrderBy 在这种情况下不必对所有项目进行排序,如果它被实现为惰性排序。标准 linq 排序并不懒惰,因此它会对所有内容进行排序。您的解决方案尽可能快,您找不到更好的解决方案。只需将其设为独立功能即可进行清理,如下所示:

 public class FriendList
        {
            public List<string> friends { get; set; } // List of friends names
            public DateTime timestamp { get; set; } // date/time on the data file
        }

        public static Tuple<T, T> GetTwoBiggest<T>(IEnumerable<T> array, Comparison<T> comp)
        {
            var enumerator = array.GetEnumerator();

            if (!enumerator.MoveNext()) { throw new ArgumentException("We need collection with at least two items"); }
            T max1 = enumerator.Current;

            if (!enumerator.MoveNext()) { throw new ArgumentException("We need collection with at least two items"); }
            T max2 = enumerator.Current;

            if (comp(max1, max2) < 0)
            {
                T tmp = max1;
                max1 = max2;
                max2 = tmp;
            }

            while (enumerator.MoveNext())
            {
                T actual = enumerator.Current;
                if (comp(actual, max1) > 0)
                {
                    max2 = max1;
                    max1 = actual;
                }
                else if (comp(actual, max2) > 0)
                {
                    max2 = actual;
                }
            }

            return new Tuple<T, T>(max1, max2);
        }

        private void button6_Click(object sender, EventArgs e)
        {
            List<FriendList> list = new List<FriendList>()
            {
                new FriendList() { timestamp = new DateTime(2015,1,1) },
                new FriendList() { timestamp = new DateTime(2015,10,2) },
                new FriendList() { timestamp = new DateTime(2015,5,3) },
                new FriendList() { timestamp = new DateTime(2015,2,4) },
                new FriendList() { timestamp = new DateTime(2015,3,5) },
                new FriendList() { timestamp = new DateTime(2015,7,6) },
                new FriendList() { timestamp = new DateTime(2015,11,7) },
                new FriendList() { timestamp = new DateTime(2015,8,8) },
            };

            var twoBiggest = GetTwoBiggest(list, (a, b) => a.timestamp.CompareTo(b.timestamp));

            Console.WriteLine(twoBiggest.Item1.timestamp);
            Console.WriteLine(twoBiggest.Item2.timestamp);
        }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-04-02
    • 2022-10-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-03-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多