Có phải tốt hơn để gọi ToList () hoặc ToArray () trong các truy vấn LINQ?


519

Tôi thường gặp phải trường hợp tôi muốn từ chối một truy vấn ngay tại nơi tôi khai báo nó. Điều này thường là bởi vì tôi cần phải lặp đi lặp lại nhiều lần nó rất tốn kém để tính toán. Ví dụ:

string raw = "...";
var lines = (from l in raw.Split('\n')
             let ll = l.Trim()
             where !string.IsNullOrEmpty(ll)
             select ll).ToList();

Điều này hoạt động tốt. Nhưng nếu tôi không sửa đổi kết quả, thì tôi cũng có thể gọi ToArray()thay ToList().

Tuy nhiên, tôi tự hỏi liệu có ToArray()được thực hiện bằng cách gọi đầu tiên ToList()và do đó ít hiệu quả bộ nhớ hơn so với chỉ gọi ToList().

Tôi có điên không? Tôi có nên gọi ToArray()- an toàn và bảo mật khi biết rằng bộ nhớ sẽ không được phân bổ hai lần?


10
Nếu bạn muốn tìm hiểu những gì xảy ra đằng sau màn cửa trong .NET, tôi thực sự khuyên bạn nên sử dụng .NET Reflector
David Hedlund

32
@DavidHedlund Tôi khuyên dùng mã nguồn .net .
Gqqnbig

1
Tôi không đồng ý rằng stackoverflow.com/questions/6750447/c-toarray-performance là một bản sao của câu hỏi này mặc dù có một mối quan hệ quan trọng. Cả sử dụng bộ nhớ (câu hỏi này) và hiệu suất (câu hỏi khác) và là những cân nhắc thú vị và không cần thiết. Chúng có thể được mô tả một cách riêng biệt, nhưng cả hai nên đưa ra quyết định chọn cái này hơn cái kia. Tôi không thể đề xuất bất kỳ một trong những câu trả lời cho câu hỏi này hoặc câu hỏi khác là toàn diện. Có một số câu trả lời mà khi được thực hiện cùng nhau sẽ cung cấp một cuộc thảo luận khá đầy đủ về cách chọn cái này hơn cái kia.
steve

1
@Gqqnbig - bình luận hữu ích nhất từ ​​trước đến nay! Cảm ơn :-)
Mark Cooper

Câu trả lời:


366

Trừ khi bạn chỉ cần một mảng để đáp ứng các ràng buộc khác mà bạn nên sử dụng ToList. Trong phần lớn các kịch bản ToArraysẽ phân bổ nhiều bộ nhớ hơn ToList.

Cả hai đều sử dụng mảng để lưu trữ, nhưng ToListcó một ràng buộc linh hoạt hơn. Nó cần mảng ít nhất bằng số lượng phần tử trong bộ sưu tập. Nếu mảng lớn hơn, đó không phải là vấn đề. Tuy nhiên, ToArraycần mảng phải có kích thước chính xác với số lượng phần tử.

Để đáp ứng ràng buộc này ToArraythường không phân bổ nhiều hơn ToList. Một khi nó có một mảng đủ lớn, nó sẽ phân bổ một mảng có kích thước chính xác và sao chép các phần tử trở lại vào mảng đó. Lần duy nhất có thể tránh điều này là khi thuật toán phát triển cho mảng chỉ xảy ra trùng với số lượng phần tử cần được lưu trữ (chắc chắn là thiểu số).

BIÊN TẬP

Một vài người đã hỏi tôi về hậu quả của việc có thêm bộ nhớ không sử dụng trong List<T>giá trị.

Đây là một mối quan tâm hợp lệ. Nếu bộ sưu tập được tạo tồn tại lâu, không bao giờ được sửa đổi sau khi được tạo và có cơ hội hạ cánh cao trong heap Gen2 thì bạn có thể tốt hơn nên lấy phần phân bổ bổ sung ToArraylên phía trước.

Nói chung mặc dù tôi thấy đây là trường hợp hiếm hơn. Nó phổ biến hơn nhiều để thấy rất nhiềuToArray cuộc gọi ngay lập tức được chuyển sang sử dụng bộ nhớ trong thời gian ngắn khác trong trường hợp ToListtốt hơn nhiều.

Chìa khóa ở đây là hồ sơ, hồ sơ và sau đó hồ sơ một số chi tiết.


14
Mặt khác, liệu bộ nhớ bổ sung được phân bổ cho công việc tạo mảng có đủ điều kiện để thu gom rác hay không, trong khi đó, chi phí phụ cho Danh sách sẽ vẫn còn? Tôi nói giữ cho nó đơn giản hơn. Nếu bạn cần thêm hoặc xóa các yếu tố, có một công cụ cho việc đó. Nếu bạn không, có một công cụ khác cho việc đó. Sử dụng một trong đó có ý nghĩa. Nếu sau này, bạn phát hiện ra một vấn đề với bộ nhớ và hiệu suất, và đây là vấn đề , hãy thay đổi nó.
Anthony Pegram

1
@AnthonyPegram có đó là một xem xét hợp lệ để thực hiện. Nếu giá trị đang được sử dụng trong bộ lưu trữ dài hạn, sẽ không được sửa đổi và có khả năng sẽ biến nó thành Gen 2, bạn có thể nên trả tiền phân bổ thêm ngay bây giờ so với gây ô nhiễm cho Gen 2. IME mặc dù tôi hiếm khi thấy điều này. Nó phổ biến hơn nhiều khi thấy ToArray được chuyển ngay lập tức sang một truy vấn LINQ ngắn khác.
JaredPar

2
@AnthonyPegram tôi đã cập nhật câu trả lời của mình để đưa vào bên này của cuộc thảo luận
JaredPar

8
@JaredPar Tôi không hiểu làm thế nào ToArrayCó thể phân bổ thêm bộ nhớ nếu nó cần kích thước vị trí chính xác trong đó ToList<>rõ ràng có vị trí dự phòng tự động. (tự động tăng)
Royi Namir

5
@RoyiNamir vì trước tiên ToArray thực hiện phân bổ kiểu ToList với chi phí hoạt động, sau đó thực hiện phân bổ kích thước chính xác bổ sung.
Timbo

169

Sự khác biệt hiệu suất sẽ không đáng kể, vì List<T>được thực hiện như một mảng có kích thước động. Gọi một trong hai ToArray()(sử dụng một Buffer<T>lớp bên trong để phát triển mảng) hoặc ToList()(gọiList<T>(IEnumerable<T>) tạo) sẽ kết thúc việc đặt chúng vào một mảng và phát triển mảng cho đến khi phù hợp với tất cả chúng.

Nếu bạn muốn xác nhận cụ thể về thực tế này, hãy kiểm tra việc triển khai các phương thức được đề cập trong Reflector - bạn sẽ thấy chúng sôi lên với mã gần như giống hệt nhau.


2
Một sự thật thú vị mà tôi đã gặp là các truy vấn tương quan gây ra bằng cách sử dụng một nhóm được xác định thông qua một nhóm tham gia trong phép chiếu của bạn khiến Linq thành SQL thêm một truy vấn phụ khác để truy xuất số đếm cho nhóm đó. Tôi giả định rằng điều này có nghĩa là trong các trường hợp này, kích thước của bộ sưu tập sẽ được biết trước khi các mục được lấy và do đó một mảng có kích thước chính xác có thể được tạo trực tiếp, giúp tiết kiệm tài nguyên bộ nhớ và xử lý trong khi thực hiện kết quả.
jpierson

133
Nếu Count được biết trước, hiệu suất là như nhau. Tuy nhiên, nếu Count không được biết trước, sự khác biệt duy nhất giữa ToArray()ToList()trước đây là phần trước phải cắt bớt phần thừa, bao gồm sao chép toàn bộ mảng, trong khi phần sau không cắt phần thừa, nhưng sử dụng trung bình 25 thêm% bộ nhớ. Điều này sẽ chỉ có ý nghĩa nếu loại dữ liệu lớn struct. Chỉ là thức ăn cho suy nghĩ.
Scott Rippey

9
@EldritchConundrum 25% xuất phát từ logic này: Nếu số lượng mục không xác định, sau đó gọi ToListhoặc ToArraysẽ bắt đầu bằng cách tạo một bộ đệm nhỏ. Khi bộ đệm đó được lấp đầy, nó tăng gấp đôi công suất của bộ đệm và tiếp tục. Vì dung lượng luôn được nhân đôi, bộ đệm không sử dụng sẽ luôn nằm trong khoảng từ 0% đến 50%.
Scott Rippey

2
@ScottRippey Tôi vừa tra cứu nguồn Danh sách mới từ nguồn IEnumerable và nó kiểm tra xem IEnumerable có phải là ICollection không, và nếu có, thì nó bắt đầu bằng cách phân bổ một mảng với kích thước chính xác cần thiết từ thuộc tính Count, vì vậy điều này sẽ là trường hợp ToList () chắc chắn sẽ nhanh hơn. Một câu trả lời hoàn chỉnh có thể bao gồm thực tế đó, mặc dù tôi không nghĩ đó là trường hợp phổ biến nhất.
AndyClaw

3
@AndyClaw Cả hai ListBuffersẽ kiểm tra ICollection, trong trường hợp đó hiệu suất sẽ giống hệt nhau.
Scott Rippey

54

(bảy năm sau ...)

Một vài câu trả lời (tốt) khác đã tập trung vào sự khác biệt hiệu suất vi mô sẽ xảy ra.

Bài đăng này chỉ là một bổ sung để đề cập đến sự khác biệt về ngữ nghĩa tồn tại giữa IEnumerator<T>được tạo bởi một mảng ( T[]) so với trả về bởi a List<T>.

Minh họa tốt nhất bằng ví dụ:

IList<int> source = Enumerable.Range(1, 10).ToArray();  // try changing to .ToList()

foreach (var x in source)
{
  if (x == 5)
    source[8] *= 100;
  Console.WriteLine(x);
}

Đoạn mã trên sẽ chạy không có ngoại lệ và tạo đầu ra:

1
2
3
4
5
6
7
số 8
900
10

Điều này cho thấy rằng IEnumarator<int>trả về bởi một int[]không theo dõi liệu mảng đã được sửa đổi kể từ khi tạo ra điều tra viên.

Lưu ý rằng tôi đã khai báo biến cục bộ sourcelà một IList<int>. Theo cách đó tôi đảm bảo trình biên dịch C # không tối ưu hóa foreachcâu lệnh thành một cái gì đó tương đương với một for (var idx = 0; idx < source.Length; idx++) { /* ... */ }vòng lặp. Đây là điều mà trình biên dịch C # có thể làm nếu tôi sử dụng var source = ...;thay thế. Trong phiên bản hiện tại của .NET framework, trình liệt kê thực tế được sử dụng ở đây là kiểu tham chiếu không công khai System.SZArrayHelper+SZGenericArrayEnumerator`1[System.Int32]nhưng tất nhiên đây là chi tiết triển khai.

Bây giờ, nếu tôi đổi .ToArray()thành .ToList(), tôi chỉ nhận được:

1
2
3
4
5

theo sau là một System.InvalidOperationExceptioncâu nói nổ tung:

Bộ sưu tập đã được sửa đổi; hoạt động liệt kê có thể không thực hiện.

Trình liệt kê cơ bản trong trường hợp này là loại giá trị có thể thay đổi công khai System.Collections.Generic.List`1+Enumerator[System.Int32](được đóng hộp bên trong một IEnumerator<int>hộp trong trường hợp này vì tôi sử dụng IList<int>).

Tóm lại, điều tra viên được tạo ra bởi một ngườiList<T>theo dõi xem danh sách có thay đổi trong quá trình liệt kê hay không, trong khi điều tra viên được tạo raT[]thì không. Vì vậy, hãy xem xét sự khác biệt này khi lựa chọn giữa.ToList().ToArray().

Mọi người thường thêm một hoặc thêm để phá vỡ một bộ sưu tập theo dõi xem nó đã được sửa đổi trong suốt thời gian sống của một điều tra viên..ToArray().ToList()

(Nếu ai muốn biết thế nàoList<>theo dõi về việc liệu bộ sưu tập đã được sửa đổi, có một lĩnh vực tư nhân _versiontrong lớp này được thay đổi mọi sự List<>được cập nhật.)


28

Tôi đồng ý với @mquander rằng sự khác biệt hiệu suất nên không đáng kể. Tuy nhiên, tôi muốn đánh giá nó để chắc chắn, vì vậy tôi đã làm - và nó không đáng kể.

Testing with List<T> source:
ToArray time: 1934 ms (0.01934 ms/call), memory used: 4021 bytes/array
ToList  time: 1902 ms (0.01902 ms/call), memory used: 4045 bytes/List

Testing with array source:
ToArray time: 1957 ms (0.01957 ms/call), memory used: 4021 bytes/array
ToList  time: 2022 ms (0.02022 ms/call), memory used: 4045 bytes/List

Mỗi mảng nguồn / Danh sách có 1000 phần tử. Vì vậy, bạn có thể thấy rằng cả sự khác biệt về thời gian và bộ nhớ là không đáng kể.

Kết luận của tôi: bạn cũng có thể sử dụng ToList () , vì một hàm List<T>cung cấp nhiều chức năng hơn một mảng, trừ khi một vài byte bộ nhớ thực sự quan trọng với bạn.


1
Tôi tự hỏi nếu kết quả này sẽ khác nếu bạn sử dụng một loại lớn structthay vì một loại hoặc lớp nguyên thủy.
Scott Rippey

12
Danh sách <T> .ToList ???? Có ý nghĩa gì? Tốt hơn hết là cố gắng cung cấp một IEnumerable cho nó, nhưng không thực hiện giao diện ICollection.
Grigory

8
Tôi muốn chắc chắn rằng tôi chỉ đo thời gian của cuộc gọi ToListhoặc ToArraycuộc gọi chứ không phải liệt kê bất kỳ IEnumerable. Danh sách <T> .ToList () vẫn tạo Danh sách mới <T> - nó không chỉ đơn giản là "trả lại cái này".
EMP

23
-1 Các hành vi của ToArray()ToList()khác nhau quá nhiều khi chúng được cung cấp một ICollection<T>tham số - Chúng chỉ thực hiện một phân bổ duy nhất và một thao tác sao chép duy nhất. Cả hai List<T>Arraythực hiện ICollection<T>, do đó, điểm chuẩn của bạn hoàn toàn không hợp lệ.
Mohammad Deh Afghanistan

1
Đối với bất cứ ai quan tâm, tôi đã đăng điểm chuẩn của riêng tôi như một câu trả lời riêng biệt . Nó sử dụng .Select(i => i)để tránh ICollection<T>vấn đề triển khai và bao gồm một nhóm kiểm soát để xem bao nhiêu thời gian chỉ được lặp đi lặp lại trên nguồn IEnumerable<>ở vị trí đầu tiên.
StriplingWar Warrior

19

ToList()thường được ưa thích nếu bạn sử dụng nó trên IEnumerable<T>(ví dụ từ ORM). Nếu ban đầu không biết độ dài của chuỗi, ToArray()tạo bộ sưu tập độ dài động như Danh sách và sau đó chuyển đổi nó thành mảng, việc này sẽ mất thêm thời gian.


26
Tôi đã quyết định rằng khả năng đọc vượt qua hiệu suất trong trường hợp này. Bây giờ tôi chỉ sử dụng ToList khi tôi muốn tiếp tục thêm các yếu tố. Trong tất cả các trường hợp khác (hầu hết các trường hợp), tôi sử dụng ToArray. Nhưng cảm ơn cho đầu vào!
Frank Krueger

5
Tìm kiếm trong ILSpy, Enumerable.ToArray()các cuộc gọi new Buffer<TSource>(source).ToArray(). Trong hàm tạo Bộ đệm nếu nguồn thực hiện ICollection thì nó gọi source.CopyTo (items, 0) và sau đó .ToArray () trả về mảng các mục bên trong trực tiếp. Vì vậy, không có chuyển đổi mà mất thêm thời gian trong trường hợp đó. Nếu nguồn không triển khai ICollection thì ToArray sẽ dẫn đến một bản sao mảng để cắt bớt các vị trí không sử dụng thêm từ cuối mảng như mô tả của nhận xét của Scott Rippey ở trên.
BrandonAGr

19

Bộ nhớ sẽ luôn được phân bổ hai lần - hoặc một cái gì đó gần với điều đó. Vì bạn không thể thay đổi kích thước một mảng, cả hai phương thức sẽ sử dụng một số loại cơ chế để thu thập dữ liệu trong một bộ sưu tập đang phát triển. (Chà, Danh sách là một bộ sưu tập đang phát triển.)

Danh sách sử dụng một mảng làm bộ nhớ trong và tăng gấp đôi dung lượng khi cần. Điều này có nghĩa là trung bình 2/3 các mặt hàng đã được phân bổ lại ít nhất một lần, một nửa trong số đó được phân bổ lại ít nhất hai lần, một nửa trong số đó ít nhất là ba lần, v.v. Điều đó có nghĩa là trung bình mỗi mục được phân bổ lại 1,3 lần, không quá nhiều chi phí.

Cũng cần nhớ rằng nếu bạn đang thu thập các chuỗi, bản thân bộ sưu tập chỉ chứa các tham chiếu đến các chuỗi, bản thân các chuỗi không được phân bổ lại.


Đây có thể là một điều không biết gì để hỏi, nhưng không phải logic 2/3, 1/3, 1/6 mà bạn phác thảo cho rằng mảng Danh sách có thể được mở rộng tại chỗ không? Đó là, có không gian trống ở cuối mảng để phân bổ hiện tại không cần phải di chuyển?

@Jonof ALLTrades: Không, mảng không bao giờ được mở rộng, quản lý bộ nhớ trong .NET đơn giản là không làm điều đó. Nếu nó được mở rộng tại chỗ, sẽ không cần phân bổ lại các mặt hàng.
Guffa

À, tôi hiểu rồi: những vật phẩm không được phân bổ lại không phải làm như vậy vì chúng nằm trong phân bổ cuối cùng. Tất cả các mục được phân bổ trong các phân bổ trước đó đều được di chuyển, nhưng do sự gia tăng logarit trong chiều dài mảng, đây là một phần có thể tính toán được. Cảm ơn đã làm rõ!

19

Đó là năm 2020 bên ngoài và mọi người đang sử dụng .NET Core 3.1, vì vậy tôi đã quyết định chạy một số điểm chuẩn với Benchmark.NET.

TL; DR: ToArray () là hiệu suất tốt hơn và thực hiện công việc truyền đạt ý định tốt hơn nếu bạn không có kế hoạch thay đổi bộ sưu tập.


    [MemoryDiagnoser]
    public class Benchmarks
    {
        [Params(0, 1, 6, 10, 39, 100, 666, 1000, 1337, 10000)]
        public int Count { get; set; }

        public IEnumerable<int> Items => Enumerable.Range(0, Count);

        [Benchmark(Description = "ToArray()", Baseline = true)]
        public int[] ToArray() => Items.ToArray();

        [Benchmark(Description = "ToList()")]
        public List<int> ToList() => Items.ToList();

        public static void Main() => BenchmarkRunner.Run<Benchmarks>();
    }

Kết quả là:


    BenchmarkDotNet=v0.12.0, OS=Windows 10.0.14393.3443 (1607/AnniversaryUpdate/Redstone1)
    Intel Core i5-4460 CPU 3.20GHz (Haswell), 1 CPU, 4 logical and 4 physical cores
    Frequency=3124994 Hz, Resolution=320.0006 ns, Timer=TSC
    .NET Core SDK=3.1.100
      [Host]     : .NET Core 3.1.0 (CoreCLR 4.700.19.56402, CoreFX 4.700.19.56404), X64 RyuJIT
      DefaultJob : .NET Core 3.1.0 (CoreCLR 4.700.19.56402, CoreFX 4.700.19.56404), X64 RyuJIT


    |    Method | Count |          Mean |       Error |      StdDev |        Median | Ratio | RatioSD |   Gen 0 | Gen 1 | Gen 2 | Allocated |
    |---------- |------ |--------------:|------------:|------------:|--------------:|------:|--------:|--------:|------:|------:|----------:|
    | ToArray() |     0 |      7.357 ns |   0.2096 ns |   0.1960 ns |      7.323 ns |  1.00 |    0.00 |       - |     - |     - |         - |
    |  ToList() |     0 |     13.174 ns |   0.2094 ns |   0.1958 ns |     13.084 ns |  1.79 |    0.05 |  0.0102 |     - |     - |      32 B |
    |           |       |               |             |             |               |       |         |         |       |       |           |
    | ToArray() |     1 |     23.917 ns |   0.4999 ns |   0.4676 ns |     23.954 ns |  1.00 |    0.00 |  0.0229 |     - |     - |      72 B |
    |  ToList() |     1 |     33.867 ns |   0.7350 ns |   0.6876 ns |     34.013 ns |  1.42 |    0.04 |  0.0331 |     - |     - |     104 B |
    |           |       |               |             |             |               |       |         |         |       |       |           |
    | ToArray() |     6 |     28.242 ns |   0.5071 ns |   0.4234 ns |     28.196 ns |  1.00 |    0.00 |  0.0280 |     - |     - |      88 B |
    |  ToList() |     6 |     43.516 ns |   0.9448 ns |   1.1949 ns |     42.896 ns |  1.56 |    0.06 |  0.0382 |     - |     - |     120 B |
    |           |       |               |             |             |               |       |         |         |       |       |           |
    | ToArray() |    10 |     31.636 ns |   0.5408 ns |   0.4516 ns |     31.657 ns |  1.00 |    0.00 |  0.0331 |     - |     - |     104 B |
    |  ToList() |    10 |     53.870 ns |   1.2988 ns |   2.2403 ns |     53.415 ns |  1.77 |    0.07 |  0.0433 |     - |     - |     136 B |
    |           |       |               |             |             |               |       |         |         |       |       |           |
    | ToArray() |    39 |     58.896 ns |   0.9441 ns |   0.8369 ns |     58.548 ns |  1.00 |    0.00 |  0.0713 |     - |     - |     224 B |
    |  ToList() |    39 |    138.054 ns |   2.8185 ns |   3.2458 ns |    138.937 ns |  2.35 |    0.08 |  0.0815 |     - |     - |     256 B |
    |           |       |               |             |             |               |       |         |         |       |       |           |
    | ToArray() |   100 |    119.167 ns |   1.6195 ns |   1.4357 ns |    119.120 ns |  1.00 |    0.00 |  0.1478 |     - |     - |     464 B |
    |  ToList() |   100 |    274.053 ns |   5.1073 ns |   4.7774 ns |    272.242 ns |  2.30 |    0.06 |  0.1578 |     - |     - |     496 B |
    |           |       |               |             |             |               |       |         |         |       |       |           |
    | ToArray() |   666 |    569.920 ns |  11.4496 ns |  11.2450 ns |    571.647 ns |  1.00 |    0.00 |  0.8688 |     - |     - |    2728 B |
    |  ToList() |   666 |  1,621.752 ns |  17.1176 ns |  16.0118 ns |  1,623.566 ns |  2.85 |    0.05 |  0.8793 |     - |     - |    2760 B |
    |           |       |               |             |             |               |       |         |         |       |       |           |
    | ToArray() |  1000 |    796.705 ns |  16.7091 ns |  19.8910 ns |    796.610 ns |  1.00 |    0.00 |  1.2951 |     - |     - |    4064 B |
    |  ToList() |  1000 |  2,453.110 ns |  48.1121 ns |  65.8563 ns |  2,460.190 ns |  3.09 |    0.10 |  1.3046 |     - |     - |    4096 B |
    |           |       |               |             |             |               |       |         |         |       |       |           |
    | ToArray() |  1337 |  1,057.983 ns |  20.9810 ns |  41.4145 ns |  1,041.028 ns |  1.00 |    0.00 |  1.7223 |     - |     - |    5416 B |
    |  ToList() |  1337 |  3,217.550 ns |  62.3777 ns |  61.2633 ns |  3,203.928 ns |  2.98 |    0.13 |  1.7357 |     - |     - |    5448 B |
    |           |       |               |             |             |               |       |         |         |       |       |           |
    | ToArray() | 10000 |  7,309.844 ns | 160.0343 ns | 141.8662 ns |  7,279.387 ns |  1.00 |    0.00 | 12.6572 |     - |     - |   40064 B |
    |  ToList() | 10000 | 23,858.032 ns | 389.6592 ns | 364.4874 ns | 23,759.001 ns |  3.26 |    0.08 | 12.6343 |     - |     - |   40096 B |

    // * Hints *
    Outliers
      Benchmarks.ToArray(): Default -> 2 outliers were removed (35.20 ns, 35.29 ns)
      Benchmarks.ToArray(): Default -> 2 outliers were removed (38.51 ns, 38.88 ns)
      Benchmarks.ToList(): Default  -> 1 outlier  was  removed (64.69 ns)
      Benchmarks.ToArray(): Default -> 1 outlier  was  removed (67.02 ns)
      Benchmarks.ToArray(): Default -> 1 outlier  was  removed (130.08 ns)
      Benchmarks.ToArray(): Default -> 1 outlier  was  detected (541.82 ns)
      Benchmarks.ToArray(): Default -> 1 outlier  was  removed (7.82 us)

    // * Legends *
      Count     : Value of the 'Count' parameter
      Mean      : Arithmetic mean of all measurements
      Error     : Half of 99.9% confidence interval
      StdDev    : Standard deviation of all measurements
      Median    : Value separating the higher half of all measurements (50th percentile)
      Ratio     : Mean of the ratio distribution ([Current]/[Baseline])
      RatioSD   : Standard deviation of the ratio distribution ([Current]/[Baseline])
      Gen 0     : GC Generation 0 collects per 1000 operations
      Gen 1     : GC Generation 1 collects per 1000 operations
      Gen 2     : GC Generation 2 collects per 1000 operations
      Allocated : Allocated memory per single operation (managed only, inclusive, 1KB = 1024B)
      1 ns      : 1 Nanosecond (0.000000001 sec)

1
Nếu bạn không định thay đổi bộ sưu tập, tôi nghĩ ý định đó có thể được hiển thị tốt hơn ToImmutableArray()(từ gói System.Collections.Immutable)
Arturo Torres Sánchez

@ ArturoTorresSánchez đúng, nhưng nếu bộ sưu tập không được đưa ra ngoài một phương thức, tôi sẽ chỉ sử dụng một mảng.
Tyrrrz

2
Cám ơn vì cái này. Câu trả lời được chọn là một đối số đơn thuần và giả sử kết quả theo sau đối số đó. Để làm điều này một cách khoa học và như một phần thưởng biết có bao nhiêu sự khác biệt, chỉ có một cách thực sự để biết.
Jonas

15

Chỉnh sửa : Phần cuối của câu trả lời này không hợp lệ. Tuy nhiên, phần còn lại vẫn là thông tin hữu ích, vì vậy tôi sẽ để lại.

Tôi biết đây là một bài viết cũ, nhưng sau khi có cùng một câu hỏi và thực hiện một số nghiên cứu, tôi đã tìm thấy một điều thú vị có thể đáng để chia sẻ.

Đầu tiên, tôi đồng ý với @mquander và câu trả lời của anh ấy. Anh ấy đúng khi nói rằng hiệu suất-khôn ngoan, hai người giống hệt nhau.

Tuy nhiên, tôi đã sử dụng Reflector để xem các phương thức trong System.Linq.Enumerablekhông gian tên tiện ích mở rộng và tôi đã nhận thấy một tối ưu hóa rất phổ biến.
Bất cứ khi nào có thể, IEnumerable<T>nguồn được truyền tới IList<T>hoặc ICollection<T>để tối ưu hóa phương thức. Ví dụ, nhìn vào ElementAt(int).

Thật thú vị, Microsoft đã chọn chỉ tối ưu hóa cho IList<T>, nhưng không IList. Có vẻ như Microsoft thích sử dụng IList<T>giao diện này.

System.Arraychỉ thực hiện IList, vì vậy nó sẽ không được hưởng lợi từ bất kỳ tối ưu hóa mở rộng nào.
Do đó, tôi trình bày rằng thực tiễn tốt nhất là sử dụng .ToList()phương pháp.
Nếu bạn sử dụng bất kỳ phương thức mở rộng nào, hoặc chuyển danh sách sang phương thức khác, có khả năng nó có thể được tối ưu hóa cho một IList<T>.


16
Tôi đã làm một bài kiểm tra và phát hiện ra một điều đáng ngạc nhiên. Một mảng DOES thực hiện IList <T>! Sử dụng Reflector để phân tích System.Array chỉ cho thấy chuỗi thừa kế của IList, ICollection, IEnumerable nhưng sử dụng phản xạ thời gian chạy tôi phát hiện ra rằng chuỗi [] có chuỗi thừa kế của IList, ICollection, IEnumerable, IList <string>, ICollection <chuỗi >, IEn đếm được <chuỗi>. Do đó, tôi không có câu trả lời hay hơn @mquander!
Scott Rippey

@ScottRippey Có. Quan sát kỳ lạ mà bạn nhận thấy thực sự là một phần của "hack" - và nó cũng có một số hàm ý khá kỳ lạ liên quan đến "kích thước cố định" và các thuộc tính tương tự (với một số điểm không nhất quán tùy thuộc vào cách bạn sử dụng nó). Có một số ý kiến ​​khá lớn chạm vào chủ đề này bên trong mã nguồn .net. Xin lỗi vì không liên kết nhưng nếu tôi nhớ chính xác thì nó khá dễ tìm (bên trong lớp mảng). (Và cũng có một câu hỏi SO lớn thảo luận về sự không nhất quán .... ở đâu đó ...> __>)
AnorZaken

@ScottRippey chỉ FYI Tôi đã tìm thấy câu trả lời này có liên quan đến nhận xét của bạn: stackoverflow.com/a/4482567/2063755
David Klempfner

14

Tôi tìm thấy các điểm chuẩn khác mà mọi người đã thực hiện ở đây thiếu, vì vậy đây là vết nứt của tôi ở đó. Hãy cho tôi biết nếu bạn tìm thấy một cái gì đó sai với phương pháp của tôi.

/* This is a benchmarking template I use in LINQPad when I want to do a
 * quick performance test. Just give it a couple of actions to test and
 * it will give you a pretty good idea of how long they take compared
 * to one another. It's not perfect: You can expect a 3% error margin
 * under ideal circumstances. But if you're not going to improve
 * performance by more than 3%, you probably don't care anyway.*/
void Main()
{
    // Enter setup code here
    var values = Enumerable.Range(1, 100000)
        .Select(i => i.ToString())
        .ToArray()
        .Select(i => i);
    values.GetType().Dump();
    var actions = new[]
    {
        new TimedAction("ToList", () =>
        {
            values.ToList();
        }),
        new TimedAction("ToArray", () =>
        {
            values.ToArray();
        }),
        new TimedAction("Control", () =>
        {
            foreach (var element in values)
            {
                // do nothing
            }
        }),
        // Add tests as desired
    };
    const int TimesToRun = 1000; // Tweak this as necessary
    TimeActions(TimesToRun, actions);
}


#region timer helper methods
// Define other methods and classes here
public void TimeActions(int iterations, params TimedAction[] actions)
{
    Stopwatch s = new Stopwatch();
    int length = actions.Length;
    var results = new ActionResult[actions.Length];
    // Perform the actions in their initial order.
    for (int i = 0; i < length; i++)
    {
        var action = actions[i];
        var result = results[i] = new ActionResult { Message = action.Message };
        // Do a dry run to get things ramped up/cached
        result.DryRun1 = s.Time(action.Action, 10);
        result.FullRun1 = s.Time(action.Action, iterations);
    }
    // Perform the actions in reverse order.
    for (int i = length - 1; i >= 0; i--)
    {
        var action = actions[i];
        var result = results[i];
        // Do a dry run to get things ramped up/cached
        result.DryRun2 = s.Time(action.Action, 10);
        result.FullRun2 = s.Time(action.Action, iterations);
    }
    results.Dump();
}

public class ActionResult
{
    public string Message { get; set; }
    public double DryRun1 { get; set; }
    public double DryRun2 { get; set; }
    public double FullRun1 { get; set; }
    public double FullRun2 { get; set; }
}

public class TimedAction
{
    public TimedAction(string message, Action action)
    {
        Message = message;
        Action = action;
    }
    public string Message { get; private set; }
    public Action Action { get; private set; }
}

public static class StopwatchExtensions
{
    public static double Time(this Stopwatch sw, Action action, int iterations)
    {
        sw.Restart();
        for (int i = 0; i < iterations; i++)
        {
            action();
        }
        sw.Stop();

        return sw.Elapsed.TotalMilliseconds;
    }
}
#endregion

Bạn có thể tải xuống LINQPad Script tại đây .

Các kết quả: Hiệu suất ToArray vs ToList

Tinh chỉnh mã ở trên, bạn sẽ khám phá ra rằng:

  1. Sự khác biệt là ít quan trọng khi xử lý các mảng nhỏ hơn . Lặp lại nhiều hơn, nhưng mảng nhỏ hơn
  2. Sự khác biệt là ít quan trọng khi giao dịch với ints hơn stringlà s.
  3. Nói chung, sử dụng structs lớn thay vì stringmất nhiều thời gian hơn, nhưng thực sự không thay đổi tỷ lệ nhiều.

Điều này đồng ý với kết luận của các câu trả lời được bình chọn hàng đầu:

  1. Bạn không thể nhận thấy sự khác biệt về hiệu suất trừ khi mã của bạn thường xuyên tạo ra nhiều danh sách dữ liệu lớn. (Chỉ có chênh lệch 200ms khi tạo 1000 danh sách mỗi chuỗi 100K.)
  2. ToList() luôn chạy nhanh hơn và sẽ là lựa chọn tốt hơn nếu bạn không dự định chờ đợi kết quả trong một thời gian dài.

Cập nhật

@JonHanna chỉ ra rằng tùy thuộc vào việc triển khai Selectcó thể thực hiện ToList()hay ToArray()dự đoán trước kích thước của bộ sưu tập kết quả. Thay thế .Select(i => i)trong mã ở trên với Where(i => true) kết quả rất giống nhau tại thời điểm này, và có nhiều khả năng làm điều đó bất kể việc triển khai .NET.

Điểm chuẩn sử dụng Where thay vì Chọn


Trong .NET Core, cả hai trường hợp nên tốt hơn ở đây so với netfx, bởi vì nó sẽ nhận ra kích thước sẽ có 100000và sử dụng nó để tối ưu hóa cả hai ToList()ToArray(), ToArray()nhẹ hơn một chút vì không cần thao tác thu nhỏ mà nó cần mặt khác, đó là một nơi ToList()có lợi thế. Ví dụ trong câu hỏi vẫn sẽ mất, bởi vì Wherephương tiện dự đoán kích thước như vậy không thể được thực hiện.
Jon Hanna

@JonHanna: Cảm ơn bạn đã phản hồi nhanh chóng. Tôi không biết .NET Core đã thực hiện tối ưu hóa đó. Thật tuyệt. Trong mã của tôi, .Select(i => i)có thể được thay thế bằng .Where(i => true)để sửa cho điều đó.
StriplingWar Warrior

Có, điều đó sẽ dừng việc tối ưu hóa ảnh hưởng đến nó trên corefx. Thật thú vị khi có cả một kích thước có sức mạnh bằng hai (sẽ mang lại ToArray()lợi thế) và một kích thước không, như trên, và so sánh kết quả.
Jon Hanna

@JonHanna: Thật thú vị, ToArray() vẫn thua trong trường hợp tốt nhất. Với Math.Pow(2, 15)các phần tử, đó là (ToList: 700ms, ToArray: 900ms). Thêm một yếu tố nữa làm cho nó trở thành (ToList: 925, ToArray: 1350). Tôi tự hỏi nếu ToArrayvẫn còn sao chép mảng ngay cả khi nó đã có kích thước hoàn hảo? Có lẽ họ cho rằng đó là một sự kiện đủ hiếm hoi mà nó không đáng để có thêm điều kiện.
StriplingWar Warrior

Nó không sao chép trên kích thước chính xác phù hợp, ngay cả trước khi chúng tôi bắt đầu tối ưu hóa nó trong corefx, vì vậy đó là trường hợp nó bị phá vỡ nhiều nhất.
Jon Hanna

12

Bạn nên căn cứ vào quyết định của mình ToListhoặc ToArraydựa trên lý tưởng lựa chọn thiết kế là gì. Nếu bạn muốn một bộ sưu tập chỉ có thể được lặp lại và truy cập theo chỉ mục, hãy chọn ToArray. Nếu bạn muốn có thêm các khả năng thêm và xóa khỏi bộ sưu tập sau này mà không gặp nhiều rắc rối, thì hãy thực hiện ToList(không thực sự là bạn không thể thêm vào một mảng, nhưng đó không phải là công cụ phù hợp cho nó thường).

Nếu hiệu suất quan trọng, bạn cũng nên xem xét những gì sẽ nhanh hơn để hoạt động trên. Trên thực tế, bạn sẽ không gọi ToListhoặc ToArraymột triệu lần, nhưng có thể hoạt động trên bộ sưu tập thu được một triệu lần. Trong đó tôn trọng []là tốt hơn, vì List<>[]với một số chi phí. Xem chủ đề này để so sánh hiệu quả: Cái nào hiệu quả hơn: Danh sách <int> hoặc int []

Trong các thử nghiệm của riêng tôi một thời gian trước đây, tôi đã tìm thấy ToArraynhanh hơn. Và tôi không chắc làm thế nào sai lệch các bài kiểm tra. Sự khác biệt hiệu năng là rất không đáng kể, chỉ có thể nhận thấy nếu bạn đang chạy các truy vấn này trong một vòng lặp hàng triệu lần.


2
Có - nếu trình biên dịch biết rằng bạn đang lặp qua một mảng (chứ không phải là IEnumerable <>), nó có thể tối ưu hóa đáng kể việc lặp lại.
RobSiklos

12

Một câu trả lời rất muộn nhưng tôi nghĩ nó sẽ hữu ích cho nhân viên của Google.

Cả hai đều hút khi họ tạo ra bằng cách sử dụng linq. Cả hai đều thực hiện cùng một mã để thay đổi kích thước bộ đệm nếu cần thiết . ToArraybên trong sử dụng một lớp để chuyển đổi IEnumerable<>thành mảng, bằng cách phân bổ một mảng gồm 4 phần tử. Nếu điều đó là không đủ, nó sẽ tăng gấp đôi kích thước bằng cách tạo một mảng mới gấp đôi kích thước của hiện tại và sao chép mảng hiện tại vào nó. Cuối cùng, nó phân bổ một mảng mới của số lượng các mặt hàng của bạn. Nếu truy vấn của bạn trả về 129 phần tử thì ToArray sẽ thực hiện 6 phân bổ và hoạt động sao chép bộ nhớ để tạo một mảng phần tử 256 và hơn là một mảng khác của 129 để trả về. rất nhiều cho hiệu quả bộ nhớ.

ToList cũng làm điều tương tự, nhưng nó bỏ qua phân bổ cuối cùng vì bạn có thể thêm các mục trong tương lai. Danh sách không quan tâm nếu nó được tạo từ truy vấn linq hoặc được tạo thủ công.

để tạo Danh sách tốt hơn với bộ nhớ nhưng tệ hơn với cpu vì danh sách là giải pháp chung, mọi hành động đều yêu cầu kiểm tra phạm vi bổ sung cho kiểm tra phạm vi nội bộ của .net cho các mảng.

Vì vậy, nếu bạn sẽ lặp đi lặp lại qua tập kết quả của mình quá nhiều lần, thì mảng là tốt vì điều đó có nghĩa là kiểm tra phạm vi ít hơn danh sách và trình biên dịch thường tối ưu hóa mảng để truy cập tuần tự.

Phân bổ khởi tạo danh sách có thể tốt hơn nếu bạn chỉ định tham số dung lượng khi bạn tạo nó. Trong trường hợp này, nó sẽ phân bổ mảng chỉ một lần, giả sử bạn biết kích thước kết quả. ToListcủa linq không chỉ định quá tải để cung cấp nó, vì vậy chúng tôi phải tạo phương thức mở rộng của mình để tạo một danh sách với dung lượng nhất định và sau đó sử dụng List<>.AddRange.

Để hoàn thành câu trả lời này, tôi phải viết các câu sau đây

  1. Cuối cùng, bạn có thể sử dụng ToArray hoặc ToList, hiệu suất sẽ không quá khác biệt (xem câu trả lời của @EMP).
  2. Bạn đang sử dụng C #. Nếu bạn cần hiệu suất thì đừng lo lắng về việc viết về mã hiệu suất cao, nhưng lo lắng về việc không viết mã hiệu suất kém.
  3. Luôn nhắm mục tiêu x64 cho mã hiệu suất cao. AFAIK, x64 JIT dựa trên trình biên dịch C ++ và thực hiện một số điều thú vị như tối ưu hóa đệ quy đuôi.
  4. Với 4,5, bạn cũng có thể tận hưởng tối ưu hóa hướng dẫn hồ sơ và JIT đa lõi.
  5. Cuối cùng, bạn có thể sử dụng mẫu async / await để xử lý nó nhanh hơn.

Cả hai đều mút? Bạn có một ý tưởng thay thế không yêu cầu phân bổ bộ nhớ dự phòng không?
nawfal

Trong bối cảnh của câu hỏi, có, cả hai đều hút nhưng vì phân bổ dư thừa, và không có gì khác. Để giảm phân bổ dự phòng, người ta có thể sử dụng các danh sách được liên kết với chi phí bộ nhớ và tốc độ lặp. Vào cuối ngày, đây là những gì chúng tôi làm, chúng tôi thực hiện đánh đổi. Một ý tưởng khác nếu tạo một danh sách có dung lượng 200 (ví dụ) và sau đó tải các mục. Điều này cũng sẽ làm giảm sự dư thừa, nhưng mảng luôn nhanh hơn, vì vậy đây là một sự đánh đổi khác.
Erdogan Kurtur

Tạo danh sách 200 ? Điều đó có thể tránh thay đổi kích thước, nhưng tôi đã nói về bộ nhớ dự phòng được sử dụng. Bạn không thể giúp nó vì không có kiến ​​thức trước về kích thước có thể là gì. Bạn đã có thể chỉ định dung lượng trong hàm tạo của a List<T>, nhưng khi bạn không hoặc khi bạn không thể, bạn không thể giúp nó.
nawfal

2
dữ liệu dư thừa duy nhất trong bộ nhớ là nội dung của mảng là danh sách các con trỏ (trong trường hợp này). một triệu con trỏ 64 bit chiếm tới 8 MB bộ nhớ, không là gì so với một triệu đối tượng mà chúng trỏ tới. 200 chỉ là một con số và nó có cơ hội giảm số lượng cuộc gọi thay đổi kích thước tối đa 5 lần. và vâng, chúng tôi không thể giúp nó. chúng ta không có lựa chọn tốt hơn. Tôi không có giải pháp tốt hơn, nhưng điều này không có nghĩa là tôi không được phép nói vấn đề ở đâu.
Erdogan Kurtur

1
hmm cuối cùng nó là nơi bạn vẽ đường thẳng. Tôi thích việc thực hiện hiện tại. Giọng điệu trong câu trả lời của bạn khiến tôi nghĩ đó là lời chỉ trích chứ không phải vấn đề ở đâu :)
nawfal

7

Đây là một câu hỏi cũ - nhưng vì lợi ích của những người dùng vấp phải nó, cũng có và thay thế 'Ghi nhớ' En enable - có tác dụng lưu trữ và dừng nhiều liệt kê của câu lệnh Linq, đó là điều ToArray () và ToList () được sử dụng rất nhiều, mặc dù các thuộc tính bộ sưu tập của danh sách hoặc mảng không bao giờ được sử dụng.

Ghi nhớ có sẵn trong lib của RX / System.Interactive và được giải thích tại đây: Thêm LINQ với System.Interactive

(Từ blog của Bart De'Smet , một bài đọc rất được khuyến khích nếu bạn đang làm việc với Linq to Object rất nhiều)


4

Một tùy chọn là thêm phương thức mở rộng của riêng bạn trả về giá trị đọc ICollection<T> . Điều này có thể tốt hơn so với việc sử dụng ToListhoặc ToArraykhi bạn không muốn sử dụng các thuộc tính lập chỉ mục của một mảng / danh sách hoặc thêm / xóa khỏi danh sách.

public static class EnumerableExtension
{
    /// <summary>
    /// Causes immediate evaluation of the linq but only if required.
    /// As it returns a readonly ICollection, is better than using ToList or ToArray
    /// when you do not want to use the indexing properties of an IList, or add to the collection.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="enumerable"></param>
    /// <returns>Readonly collection</returns>
    public static ICollection<T> Evaluate<T>(this IEnumerable<T> enumerable)
    {
        //if it's already a readonly collection, use it
        var collection = enumerable as ICollection<T>;
        if ((collection != null) && collection.IsReadOnly)
        {
            return collection;
        }
        //or make a new collection
        return enumerable.ToList().AsReadOnly();
    }
}

Bài kiểm tra đơn vị:

[TestClass]
public sealed class EvaluateLinqTests
{
    [TestMethod]
    public void EvalTest()
    {
        var list = new List<int> {1, 2, 3};
        var linqResult = list.Select(i => i);
        var linqResultEvaluated = list.Select(i => i).Evaluate();
        list.Clear();
        Assert.AreEqual(0, linqResult.Count());
        //even though we have cleared the underlying list, the evaluated list does not change
        Assert.AreEqual(3, linqResultEvaluated.Count());
    }

    [TestMethod]
    public void DoesNotSaveCreatingListWhenHasListTest()
    {
        var list = new List<int> {1, 2, 3};
        var linqResultEvaluated = list.Evaluate();
        //list is not readonly, so we expect a new list
        Assert.AreNotSame(list, linqResultEvaluated);
    }

    [TestMethod]
    public void SavesCreatingListWhenHasReadonlyListTest()
    {
        var list = new List<int> {1, 2, 3}.AsReadOnly();
        var linqResultEvaluated = list.Evaluate();
        //list is readonly, so we don't expect a new list
        Assert.AreSame(list, linqResultEvaluated);
    }

    [TestMethod]
    public void SavesCreatingListWhenHasArrayTest()
    {
        var list = new[] {1, 2, 3};
        var linqResultEvaluated = list.Evaluate();
        //arrays are readonly (wrt ICollection<T> interface), so we don't expect a new object
        Assert.AreSame(list, linqResultEvaluated);
    }

    [TestMethod]
    [ExpectedException(typeof (NotSupportedException))]
    public void CantAddToResultTest()
    {
        var list = new List<int> {1, 2, 3};
        var linqResultEvaluated = list.Evaluate();
        Assert.AreNotSame(list, linqResultEvaluated);
        linqResultEvaluated.Add(4);
    }

    [TestMethod]
    [ExpectedException(typeof (NotSupportedException))]
    public void CantRemoveFromResultTest()
    {
        var list = new List<int> {1, 2, 3};
        var linqResultEvaluated = list.Evaluate();
        Assert.AreNotSame(list, linqResultEvaluated);
        linqResultEvaluated.Remove(1);
    }
}

Điều đáng chú ý là hợp đồng thu thập chỉ đọc chỉ quy định rằng người dùng của đối tượng có thể không sửa đổi nó, nhưng chủ sở hữu vẫn có thể làm như vậy nếu nó giữ một tham chiếu đến nó cung cấp giao diện có thể thay đổi. Đối với các giao diện đảm bảo rằng cấu trúc cơ bản sẽ không bao giờ thay đổi, hãy xem các bộ sưu tập bất biến. Về lý do tại sao các bộ sưu tập đọc-ghi không thay đổi, hoặc chỉ đọc hoặc đơn giản là tốt hơn hoặc xấu hơn, người ta cần một điểm tham chiếu để so sánh; không có câu trả lời cuối cùng (chúng tôi sẽ không phải chọn).
tne

@tne Lưu ý Tôi thực hiện Tolist trước AsReadOnly, vì vậy không có tài liệu tham khảo nào về khả năng biến đổi cơ bản.
weston

Bạn hoàn toàn đúng, và đó có lẽ là cách tốt nhất để làm mọi việc trước khi các bộ sưu tập bất biến đến BCL (Tôi thấy bản beta đầu tiên xuất hiện một tháng sau câu trả lời của bạn).
tne

Các bộ sưu tập bất biến tồn tại vì sự an toàn của luồng, trong đó các luồng có thể cho rằng nó sẽ không thay đổi và nếu có, một phiên bản mới được tạo ra, thay vì chạy đua với độc giả và thay đổi nó trong khi họ sử dụng nó. Bằng cách này, không ai cần phải có được một khóa.
doug65536

4

ToListAsync<T>() được ưa thích.

Trong Entity Framework 6, cả hai phương thức cuối cùng đều gọi đến cùng một phương thức bên trong, nhưng ToArrayAsync<T>()các cuộc gọi list.ToArray()ở cuối, được thực hiện như là

T[] array = new T[_size];
Array.Copy(_items, 0, array, 0, _size);
return array;

Vì vậy, ToArrayAsync<T>()có một số chi phí, do đó ToListAsync<T>()được ưa thích.


1
Đó thực sự là câu trả lời mà tôi đang tìm kiếm, làm thế nào để EF thực hiện nó. Tôi tò mò về nó như thế nào trong EF Core.
Shimmy Weitzhandler

3

Câu hỏi cũ nhưng người hỏi mới mọi lúc.

Theo nguồn của System.Linq.Enumerable , ToListchỉ cần trả về a new List(source), trong khi ToArraysử dụng a new Buffer<T>(source).ToArray()để trả về a T[].

Về phân bổ bộ nhớ:

Trong khi chạy trên một đối tượng IEnumerable<T>duy nhất , ToArrayhãy phân bổ bộ nhớ một lần nữa ToList. Nhưng bạn không cần phải quan tâm đến nó trong hầu hết các trường hợp, bởi vì GC sẽ thực hiện việc thu gom rác khi cần thiết.

Về thời gian chạy hiệu quả:

Những người đang đặt câu hỏi này có thể chạy đoạn mã sau trên máy của chính bạn và bạn sẽ nhận được câu trả lời của mình.

class PersonC
{
    public Guid uuid;
    public string name;
    public int age;
    public bool sex;
    public DateTime BirthDay;
    public double weight;
}

struct PersonS
{
    public Guid uuid;
    public string name;
    public int age;
    public bool sex;
    public DateTime BirthDay;
    public double weight;
}

class PersonT<T> : IEnumerable<T>
{
    private List<T> items;
    public PersonT(IEnumerable<T> init)
    {
        items = new List<T>(init);
    }

    public IEnumerator<T> GetEnumerator() => items.GetEnumerator();
    IEnumerator IEnumerable.GetEnumerator() => items.GetEnumerator();
}

private IEnumerable<PersonC> C(int count)
{
    for (var i = 0; i < count; ++i)
    {
        var guid = Guid.NewGuid();
        var guidBytes = guid.ToByteArray(); //16 bytes
        yield return new PersonC
        {
            uuid = guid,
            name = guid.ToString(),
            age = guidBytes[0] ^ guidBytes[7],
            sex = guidBytes[14] % 2 == 0,
            BirthDay = DateTime.Now.AddDays(-guidBytes[11] * 18),
            weight = guidBytes[12] * 100
        };
    }
}

private IEnumerable<PersonS> S(int count)
{
    for (var i = 0; i < count; ++i)
    {
        var guid = Guid.NewGuid();
        var guidBytes = guid.ToByteArray(); //16 bytes
        yield return new PersonS
        {
            uuid = guid,
            name = guid.ToString(),
            age = guidBytes[0] ^ guidBytes[7],
            sex = guidBytes[14] % 2 == 0,
            BirthDay = DateTime.Now.AddDays(-guidBytes[11] * 18),
            weight = guidBytes[12] * 100
        };
    }
}

private void MakeLog(string test, List<long> log) =>
    Console.WriteLine("{0} {1} ms -> [{2}]",
        test,
        log.Average(),
        string.Join(", ", log)
    );

private void Test1(int times, int count)
{
    var test = Enumerable.Range(1, times).ToArray();

    MakeLog("C.ToList", test.Select(o =>
    {
        var sw = new Stopwatch();
        GC.Collect();
        sw.Start();
        var ret = C(count).ToList();
        sw.Stop();
        return sw.ElapsedMilliseconds;
    }).ToList());

    MakeLog("C.ToArray", test.Select(o =>
    {
        var sw = new Stopwatch();
        GC.Collect();
        sw.Start();
        var ret = C(count).ToArray();
        sw.Stop();
        return sw.ElapsedMilliseconds;
    }).ToList());

    MakeLog("S.ToList", test.Select(o =>
    {
        var sw = new Stopwatch();
        GC.Collect();
        sw.Start();
        var ret = S(count).ToList();
        sw.Stop();
        return sw.ElapsedMilliseconds;
    }).ToList());

    MakeLog("S.ToArray", test.Select(o =>
    {
        var sw = new Stopwatch();
        GC.Collect();
        sw.Start();
        var ret = S(count).ToArray();
        sw.Stop();
        return sw.ElapsedMilliseconds;
    }).ToList());
}

private void Test2(int times, int count)
{
    var test = Enumerable.Range(1, times).ToArray();

    var dataC1 = new PersonT<PersonC>(C(count));
    var dataS1 = new PersonT<PersonS>(S(count));

    MakeLog("C1.ToList", test.Select(o =>
    {
        var sw = new Stopwatch();
        GC.Collect();
        sw.Start();
        var ret = dataC1.ToList();
        sw.Stop();
        return sw.ElapsedMilliseconds;
    }).ToList());

    MakeLog("C1.ToArray", test.Select(o =>
    {
        var sw = new Stopwatch();
        GC.Collect();
        sw.Start();
        var ret = dataC1.ToArray();
        sw.Stop();
        return sw.ElapsedMilliseconds;
    }).ToList());

    MakeLog("S1.ToList", test.Select(o =>
    {
        var sw = new Stopwatch();
        GC.Collect();
        sw.Start();
        var ret = dataS1.ToList();
        sw.Stop();
        return sw.ElapsedMilliseconds;
    }).ToList());

    MakeLog("S1.ToArray", test.Select(o =>
    {
        var sw = new Stopwatch();
        GC.Collect();
        sw.Start();
        var ret = dataS1.ToArray();
        sw.Stop();
        return sw.ElapsedMilliseconds;
    }).ToList());
}

private void Test3(int times, int count)
{
    var test = Enumerable.Range(1, times).ToArray();

    var dataC2 = (ICollection<PersonC>) new List<PersonC>(C(count));
    var dataS2 = (ICollection<PersonS>) new List<PersonS>(S(count));

    MakeLog("C2.ToList", test.Select(o =>
    {
        var sw = new Stopwatch();
        GC.Collect();
        sw.Start();
        var ret = dataC2.ToList();
        sw.Stop();
        return sw.ElapsedMilliseconds;
    }).ToList());

    MakeLog("C2.ToArray", test.Select(o =>
    {
        var sw = new Stopwatch();
        GC.Collect();
        sw.Start();
        var ret = dataC2.ToArray();
        sw.Stop();
        return sw.ElapsedMilliseconds;
    }).ToList());

    MakeLog("S2.ToList", test.Select(o =>
    {
        var sw = new Stopwatch();
        GC.Collect();
        sw.Start();
        var ret = dataS2.ToList();
        sw.Stop();
        return sw.ElapsedMilliseconds;
    }).ToList());

    MakeLog("S2.ToArray", test.Select(o =>
    {
        var sw = new Stopwatch();
        GC.Collect();
        sw.Start();
        var ret = dataS2.ToArray();
        sw.Stop();
        return sw.ElapsedMilliseconds;
    }).ToList());
}

private void TestMain()
{
    const int times = 100;
    const int count = 1_000_000 + 1;
    Test1(times, count);
    Test2(times, count);
    Test3(times, count);
}

Tôi đã nhận được những kết quả này trên máy của mình:

Nhóm 1:

C.ToList 761.79 ms -> [775, 755, 759, 759, 756, 759, 765, 750, 757, 762, 759, 754, 757, 753, 763, 753, 759, 756, 768, 754, 763, 757, 757, 777, 780, 758, 754, 758, 762, 754, 758, 757, 763, 758, 760, 754, 761, 755, 764, 847, 952, 755, 747, 763, 760, 758, 754, 763, 761, 758, 750, 764, 757, 763, 762, 756, 753, 759, 759, 757, 758, 779, 765, 760, 760, 756, 760, 756, 755, 764, 759, 753, 757, 760, 752, 764, 758, 760, 758, 760, 755, 761, 751, 753, 761, 762, 761, 758, 759, 752, 765, 756, 760, 755, 757, 753, 760, 751, 755, 779]
C.ToArray 782.56 ms -> [783, 774, 771, 771, 773, 774, 775, 775, 772, 770, 771, 774, 771, 1023, 975, 772, 767, 776, 771, 779, 772, 779, 775, 771, 775, 773, 775, 771, 765, 774, 770, 781, 772, 771, 781, 762, 817, 770, 775, 779, 769, 774, 763, 775, 777, 769, 777, 772, 775, 778, 775, 771, 770, 774, 772, 769, 772, 769, 774, 775, 768, 775, 769, 774, 771, 776, 774, 773, 778, 769, 778, 767, 770, 787, 783, 779, 771, 768, 805, 780, 779, 767, 773, 771, 773, 785, 1044, 853, 775, 774, 775, 771, 770, 769, 770, 776, 770, 780, 821, 770]
S.ToList 704.2 ms -> [687, 702, 709, 691, 694, 710, 696, 698, 700, 694, 701, 719, 706, 694, 702, 699, 699, 703, 704, 701, 703, 705, 697, 707, 691, 697, 707, 692, 721, 698, 695, 700, 704, 700, 701, 710, 700, 705, 697, 711, 694, 700, 695, 698, 701, 692, 696, 702, 690, 699, 708, 700, 703, 714, 701, 697, 700, 699, 694, 701, 697, 696, 699, 694, 709, 1068, 690, 706, 699, 699, 695, 708, 695, 704, 704, 700, 695, 704, 695, 696, 702, 700, 710, 708, 693, 697, 702, 694, 700, 706, 699, 695, 706, 714, 704, 700, 695, 697, 707, 704]
S.ToArray 742.5 ms -> [742, 743, 733, 745, 741, 724, 738, 745, 728, 732, 740, 727, 739, 740, 726, 744, 758, 732, 744, 745, 730, 739, 738, 723, 745, 757, 729, 741, 736, 724, 744, 756, 739, 766, 737, 725, 741, 742, 736, 748, 742, 721, 746, 1043, 806, 747, 731, 727, 742, 742, 726, 738, 746, 727, 739, 743, 730, 744, 753, 741, 739, 746, 728, 740, 744, 734, 734, 738, 731, 747, 736, 731, 765, 735, 726, 740, 743, 730, 746, 742, 725, 731, 757, 734, 738, 741, 732, 747, 744, 721, 742, 741, 727, 745, 740, 730, 747, 760, 737, 740]

C1.ToList 32.34 ms -> [35, 31, 31, 31, 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 33, 32, 31, 31, 31, 31, 30, 32, 31, 31, 31, 31, 32, 30, 31, 31, 31, 30, 32, 31, 31, 31, 36, 31, 31, 31, 32, 30, 31, 32, 31, 31, 31, 31, 31, 32, 31, 31, 31, 31, 33, 32, 31, 32, 31, 31, 33, 31, 31, 31, 31, 31, 32, 31, 32, 31, 34, 38, 68, 42, 79, 33, 31, 31, 31, 31, 31, 30, 30, 30, 30, 31, 31, 31, 31, 32, 31, 32, 31, 31, 31, 32, 33, 33, 31, 31]
C1.ToArray 56.32 ms -> [57, 56, 59, 54, 54, 55, 56, 57, 54, 54, 55, 55, 57, 56, 59, 57, 56, 58, 56, 56, 54, 56, 57, 55, 55, 55, 57, 58, 57, 58, 55, 55, 56, 55, 57, 56, 56, 59, 56, 56, 56, 56, 58, 56, 57, 56, 56, 57, 56, 55, 56, 56, 56, 59, 56, 56, 56, 55, 55, 54, 55, 54, 57, 56, 56, 56, 55, 55, 56, 56, 56, 59, 56, 56, 57, 56, 57, 56, 56, 56, 56, 62, 55, 56, 56, 56, 69, 57, 58, 56, 57, 58, 56, 57, 56, 56, 56, 56, 56, 56]
S1.ToList 88.69 ms -> [96, 90, 90, 89, 91, 88, 89, 90, 96, 89, 89, 89, 90, 90, 90, 89, 90, 90, 89, 90, 89, 91, 89, 91, 89, 91, 89, 90, 90, 89, 87, 88, 87, 88, 87, 87, 87, 87, 88, 88, 87, 87, 89, 87, 87, 87, 91, 88, 87, 86, 89, 87, 90, 89, 89, 90, 89, 87, 87, 87, 86, 87, 88, 90, 88, 87, 87, 92, 87, 87, 88, 88, 88, 86, 86, 87, 88, 87, 87, 87, 89, 87, 89, 87, 90, 89, 89, 89, 91, 89, 90, 89, 90, 88, 90, 90, 90, 88, 89, 89]
S1.ToArray 143.26 ms -> [130, 129, 130, 131, 133, 130, 131, 130, 135, 137, 130, 136, 132, 131, 130, 131, 132, 130, 132, 136, 130, 131, 157, 153, 194, 364, 176, 189, 203, 194, 189, 192, 183, 140, 142, 147, 145, 134, 159, 158, 142, 167, 130, 143, 145, 144, 160, 154, 156, 153, 153, 164, 142, 145, 137, 134, 145, 143, 142, 135, 133, 133, 135, 134, 134, 139, 139, 133, 134, 141, 133, 132, 133, 132, 133, 131, 135, 132, 133, 132, 128, 128, 130, 132, 129, 129, 129, 129, 129, 128, 134, 129, 129, 129, 129, 128, 128, 137, 130, 131]

C2.ToList 3.25 ms -> [5, 3, 3, 3, 3, 4, 3, 4, 3, 4, 3, 3, 3, 4, 4, 3, 3, 3, 4, 3, 3, 3, 3, 4, 3, 4, 3, 4, 3, 3, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 3, 4, 3, 3, 3, 3, 3, 4, 3, 4, 3, 3, 3, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 3, 4, 3, 4, 3, 3, 3, 4, 3, 3, 4, 3, 3, 3, 4, 3, 3, 3, 3, 3]
C2.ToArray 3.37 ms -> [4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 3, 4, 3, 5, 4, 9, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 3, 3, 3, 3, 3, 4, 4, 3, 3, 4, 4, 3, 3, 3, 4, 3, 3, 3, 3, 4, 3, 3, 3, 3, 3, 4, 4, 3, 4, 4, 3, 3, 4, 3, 3, 4, 3, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 3, 3, 4, 3, 3, 3, 3, 3, 4, 4, 3, 3, 3, 3, 3, 4, 3, 4, 3, 3, 3]
S2.ToList 37.72 ms -> [38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 40, 38, 38, 39, 39, 38, 38, 38, 38, 37, 37, 37, 37, 39, 37, 37, 39, 38, 37, 37, 37, 37, 39, 38, 37, 37, 38, 37, 38, 37, 37, 38, 37, 37, 37, 38, 37, 37, 36, 37, 38, 37, 39, 37, 39, 38, 37, 38, 38, 38, 38, 38, 38, 37, 38, 38, 38, 38, 38, 37, 38, 37, 37, 38, 37, 37, 39, 41, 37, 38, 38, 37, 37, 37, 37, 38, 37, 37, 37, 40, 37, 37, 37, 37, 39, 38]
S2.ToArray 38.86 ms -> [39, 37, 39, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 39, 38, 38, 38, 38, 38, 38, 38, 38, 39, 39, 38, 38, 38, 39, 37, 38, 38, 38, 38, 38, 37, 37, 38, 37, 37, 38, 38, 40, 38, 38, 38, 38, 38, 39, 38, 38, 39, 38, 38, 39, 38, 38, 40, 38, 39, 38, 38, 39, 38, 38, 38, 38, 38, 39, 38, 38, 38, 39, 39, 37, 38, 38, 39, 71, 78, 37, 37, 37, 39, 38, 38, 39, 38, 38, 38, 38, 38, 39, 38, 38, 38, 39, 38, 38, 38]

Nhóm2:

C.ToList 756.81 ms
C.ToArray 774.21 ms
S.ToList 709.7 ms
S.ToArray 753.51 ms

C1.ToList 32.06 ms
C1.ToArray 56.58 ms
S1.ToList 89.43 ms
S1.ToArray 132.85 ms

C2.ToList 3.45 ms
C2.ToArray 3.36 ms
S2.ToList 41.43 ms
S2.ToArray 40.84 ms

Nhóm 3:

C.ToList 756.64 ms
C.ToArray 771.56 ms
S.ToList 705.42 ms
S.ToArray 749.59 ms

C1.ToList 31.45 ms
C1.ToArray 57.03 ms
S1.ToList 91.26 ms
S1.ToArray 129.77 ms

C2.ToList 3.26 ms
C2.ToArray 3.29 ms
S2.ToList 41.57 ms
S2.ToArray 40.69 ms

Nhóm 4:

C.ToList 729.65 ms -> [749, 730, 721, 719, 723, 743, 721, 724, 727, 722, 716, 725, 723, 726, 718, 722, 731, 722, 723, 725, 723, 722, 728, 726, 728, 718, 726, 1088, 788, 737, 729, 710, 730, 728, 717, 723, 728, 721, 722, 728, 722, 736, 723, 729, 732, 724, 726, 727, 728, 728, 726, 726, 725, 727, 725, 728, 728, 718, 724, 725, 726, 724, 726, 729, 727, 722, 722, 725, 725, 728, 724, 727, 738, 717, 726, 723, 725, 725, 727, 724, 720, 726, 726, 723, 727, 730, 723, 721, 725, 727, 727, 733, 720, 722, 722, 725, 722, 725, 728, 726]
C.ToArray 788.36 ms -> [748, 740, 742, 797, 1090, 774, 781, 787, 784, 786, 786, 782, 781, 781, 784, 783, 783, 781, 783, 787, 783, 784, 775, 789, 784, 785, 778, 774, 781, 783, 786, 781, 780, 788, 778, 785, 777, 781, 786, 782, 781, 787, 782, 787, 784, 773, 783, 782, 781, 777, 783, 781, 785, 788, 777, 776, 784, 784, 783, 789, 778, 781, 791, 768, 779, 783, 781, 787, 786, 781, 784, 781, 785, 781, 780, 809, 1155, 780, 790, 789, 783, 776, 785, 783, 786, 787, 782, 782, 787, 777, 779, 784, 783, 776, 786, 775, 782, 779, 784, 784]
S.ToList 705.54 ms -> [690, 705, 709, 708, 702, 707, 703, 696, 703, 702, 700, 703, 700, 707, 705, 699, 697, 703, 695, 698, 707, 697, 711, 710, 699, 700, 708, 707, 693, 710, 704, 691, 702, 700, 703, 700, 705, 700, 703, 695, 709, 705, 698, 699, 709, 700, 699, 704, 691, 705, 703, 700, 708, 1048, 710, 706, 706, 692, 702, 705, 695, 701, 710, 697, 698, 706, 705, 707, 707, 695, 698, 704, 698, 699, 705, 698, 703, 702, 701, 697, 702, 702, 704, 703, 699, 707, 703, 705, 701, 717, 698, 695, 713, 696, 708, 705, 697, 699, 700, 698]
S.ToArray 745.01 ms -> [751, 743, 727, 734, 736, 745, 739, 750, 739, 750, 758, 739, 744, 738, 730, 744, 745, 739, 744, 750, 733, 735, 743, 731, 749, 748, 727, 746, 749, 731, 737, 803, 1059, 756, 769, 748, 740, 745, 741, 746, 749, 732, 741, 742, 732, 744, 746, 737, 742, 739, 733, 744, 741, 729, 746, 760, 725, 741, 764, 739, 750, 751, 727, 745, 738, 727, 735, 741, 720, 736, 740, 733, 741, 746, 731, 749, 756, 740, 738, 736, 732, 741, 741, 733, 741, 744, 736, 742, 742, 735, 743, 746, 729, 748, 765, 743, 734, 742, 728, 749]

C1.ToList 32.27 ms -> [36, 31, 31, 32, 31, 32, 31, 30, 32, 30, 30, 30, 34, 32, 31, 31, 31, 31, 31, 31, 31, 32, 38, 51, 68, 57, 35, 30, 31, 31, 30, 30, 33, 30, 31, 34, 31, 34, 32, 31, 31, 31, 31, 32, 30, 30, 31, 30, 31, 31, 32, 31, 31, 31, 32, 31, 31, 31, 32, 31, 33, 31, 31, 32, 30, 30, 30, 30, 30, 33, 30, 33, 32, 31, 30, 31, 31, 32, 32, 31, 35, 31, 34, 31, 31, 32, 31, 31, 32, 31, 32, 31, 31, 35, 31, 31, 31, 31, 31, 32]
C1.ToArray 56.72 ms -> [58, 56, 57, 57, 59, 58, 58, 57, 56, 59, 57, 55, 55, 54, 56, 55, 56, 56, 57, 59, 56, 55, 58, 56, 55, 55, 55, 55, 58, 58, 55, 57, 57, 56, 57, 57, 57, 57, 59, 59, 56, 57, 56, 57, 57, 56, 57, 59, 58, 56, 57, 57, 57, 58, 56, 56, 59, 56, 59, 57, 57, 57, 57, 59, 57, 56, 57, 56, 58, 56, 57, 56, 57, 59, 55, 58, 55, 55, 56, 56, 56, 56, 56, 56, 56, 56, 56, 57, 56, 56, 57, 56, 56, 57, 58, 57, 57, 57, 57, 57]
S1.ToList 90.72 ms -> [95, 90, 90, 89, 89, 89, 91, 89, 89, 87, 91, 89, 89, 89, 91, 89, 89, 89, 90, 89, 89, 90, 88, 89, 88, 90, 89, 90, 89, 89, 90, 90, 89, 89, 90, 91, 89, 91, 89, 90, 89, 89, 90, 91, 89, 89, 89, 89, 89, 89, 90, 89, 89, 89, 90, 89, 90, 89, 91, 89, 90, 89, 90, 89, 90, 89, 96, 89, 90, 89, 89, 89, 89, 89, 90, 89, 89, 89, 90, 87, 89, 90, 90, 91, 89, 91, 89, 89, 90, 91, 90, 89, 93, 144, 149, 90, 90, 89, 89, 89]
S1.ToArray 131.4 ms -> [130, 128, 127, 134, 129, 129, 130, 136, 131, 130, 132, 132, 133, 131, 132, 131, 133, 132, 130, 131, 132, 131, 130, 133, 133, 130, 130, 131, 131, 131, 132, 134, 131, 131, 132, 131, 132, 131, 134, 131, 131, 130, 131, 131, 130, 132, 129, 131, 131, 131, 132, 131, 133, 134, 131, 131, 132, 132, 131, 133, 131, 131, 130, 133, 131, 130, 134, 132, 131, 132, 132, 131, 131, 134, 131, 131, 132, 132, 131, 130, 138, 130, 130, 131, 132, 132, 130, 134, 131, 131, 132, 131, 130, 132, 133, 131, 131, 131, 130, 131]

C2.ToList 3.21 ms -> [4, 3, 3, 3, 4, 3, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 3, 3, 3, 3, 4, 3, 3, 3, 3, 3, 4, 3, 4, 3, 3, 3, 3, 3, 4, 3, 3, 4, 3, 3, 3, 3, 3, 3, 3, 4, 3, 3, 4, 3, 3, 3, 3, 3, 3, 4, 3, 4, 3, 3, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 3, 3, 3, 4, 4, 3, 3, 3, 3, 3, 4, 3, 3, 3]
C2.ToArray 3.22 ms -> [4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 5, 4, 3, 3, 3, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 3, 3, 3, 3, 3, 3, 4, 3, 4, 3, 3, 3, 4, 3, 4, 3, 3, 3, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 3, 3, 3, 4, 3, 4, 3, 3, 3, 3, 3, 4, 3, 3, 3, 4, 3, 4, 3, 3, 3, 3, 4]
S2.ToList 41.46 ms -> [42, 40, 41, 40, 42, 40, 40, 40, 40, 40, 40, 40, 40, 41, 40, 40, 41, 40, 40, 40, 39, 41, 41, 39, 40, 40, 43, 40, 39, 40, 40, 40, 40, 40, 40, 41, 40, 40, 40, 43, 40, 43, 75, 76, 47, 39, 40, 40, 40, 40, 42, 40, 41, 40, 40, 40, 44, 41, 40, 42, 42, 40, 41, 41, 41, 41, 41, 40, 41, 41, 41, 41, 42, 41, 40, 41, 41, 42, 42, 41, 40, 41, 41, 41, 41, 41, 40, 42, 40, 42, 41, 41, 41, 43, 41, 41, 41, 41, 42, 41]
S2.ToArray 41.14 ms -> [42, 41, 41, 40, 40, 40, 40, 41, 41, 42, 41, 42, 41, 41, 41, 42, 41, 41, 42, 41, 41, 41, 41, 41, 42, 40, 41, 40, 42, 40, 42, 41, 40, 42, 41, 41, 43, 42, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 40, 40, 41, 41, 41, 40, 42, 41, 41, 41, 41, 41, 40, 41, 41, 42, 41, 41, 41, 42, 41, 41, 41, 41, 41, 41, 42, 42, 42, 41, 45, 46, 41, 40, 41, 41, 42, 41, 41, 41, 41, 41, 41, 40, 41, 43, 40, 40, 40, 40, 43, 41]

Nhóm 5:

C.ToList 757.06 ms -> [770, 752, 752, 751, 778, 763, 761, 763, 747, 758, 748, 747, 754, 749, 752, 753, 756, 762, 750, 753, 756, 749, 755, 757, 755, 756, 755, 744, 753, 758, 747, 751, 759, 751, 761, 755, 746, 752, 752, 749, 746, 752, 753, 755, 752, 755, 754, 754, 966, 937, 749, 759, 748, 747, 754, 749, 755, 750, 746, 754, 757, 752, 753, 745, 758, 755, 761, 753, 751, 755, 755, 752, 746, 756, 755, 746, 742, 751, 751, 749, 752, 751, 756, 756, 755, 742, 749, 754, 749, 756, 753, 751, 754, 752, 751, 754, 753, 749, 755, 756]
C.ToArray 772.8 ms -> [766, 772, 755, 763, 758, 767, 763, 762, 761, 768, 769, 763, 770, 757, 765, 760, 766, 759, 764, 761, 760, 777, 1102, 881, 759, 765, 758, 762, 772, 761, 758, 757, 765, 769, 769, 761, 762, 762, 763, 760, 770, 764, 760, 768, 758, 766, 763, 770, 769, 761, 764, 761, 761, 767, 761, 762, 764, 757, 765, 766, 767, 771, 753, 762, 769, 768, 759, 764, 764, 760, 763, 763, 763, 763, 763, 767, 761, 771, 760, 765, 760, 758, 768, 770, 751, 771, 767, 771, 765, 763, 760, 765, 765, 769, 767, 767, 1193, 774, 767, 764]
S.ToList 704.73 ms -> [682, 708, 705, 699, 705, 704, 695, 703, 702, 699, 701, 708, 699, 702, 703, 701, 701, 699, 701, 707, 707, 700, 701, 705, 700, 697, 706, 702, 701, 706, 699, 692, 702, 697, 707, 704, 697, 698, 699, 699, 702, 703, 698, 697, 702, 703, 702, 704, 694, 697, 707, 695, 711, 710, 700, 693, 703, 699, 699, 706, 698, 701, 703, 704, 698, 706, 700, 704, 701, 699, 702, 705, 694, 698, 709, 736, 1053, 704, 694, 700, 698, 696, 701, 700, 700, 706, 706, 692, 698, 707, 703, 695, 703, 699, 694, 708, 695, 694, 706, 695]
S.ToArray 744.17 ms -> [746, 740, 725, 740, 739, 731, 746, 760, 735, 738, 740, 734, 744, 748, 737, 744, 745, 727, 736, 738, 728, 743, 745, 735, 748, 760, 739, 748, 762, 742, 741, 747, 733, 746, 758, 742, 742, 741, 724, 744, 747, 727, 740, 740, 729, 742, 757, 741, 740, 742, 726, 739, 746, 1133, 749, 737, 730, 740, 747, 733, 747, 752, 731, 747, 742, 730, 741, 749, 731, 749, 743, 730, 747, 742, 731, 737, 745, 734, 739, 735, 727, 743, 752, 731, 744, 742, 729, 740, 746, 731, 739, 746, 733, 745, 743, 733, 739, 742, 727, 737]

C1.ToList 31.71 ms -> [35, 32, 32, 30, 31, 33, 31, 32, 32, 31, 31, 32, 32, 33, 32, 31, 31, 32, 31, 32, 32, 32, 31, 32, 33, 32, 31, 31, 31, 32, 31, 34, 31, 31, 32, 33, 32, 32, 31, 32, 34, 32, 31, 32, 33, 31, 32, 32, 31, 32, 32, 32, 32, 32, 32, 31, 31, 32, 31, 33, 30, 31, 32, 30, 30, 33, 32, 32, 34, 31, 31, 31, 31, 32, 31, 31, 31, 31, 32, 31, 31, 33, 31, 32, 32, 32, 33, 32, 31, 31, 31, 31, 31, 32, 32, 33, 32, 31, 31, 32]
C1.ToArray 59.53 ms -> [63, 57, 58, 58, 57, 59, 59, 57, 60, 131, 127, 67, 58, 56, 59, 56, 57, 58, 58, 58, 57, 59, 60, 57, 57, 59, 60, 57, 57, 57, 58, 58, 58, 58, 57, 57, 61, 57, 58, 57, 57, 57, 57, 57, 58, 58, 58, 58, 57, 58, 59, 57, 58, 57, 57, 59, 58, 58, 59, 57, 59, 57, 56, 56, 59, 56, 56, 59, 57, 58, 58, 58, 57, 58, 59, 59, 58, 57, 58, 62, 65, 57, 57, 57, 58, 60, 59, 58, 59, 57, 58, 57, 58, 59, 58, 58, 58, 59, 60, 58]
S1.ToList 82.78 ms -> [87, 82, 83, 83, 82, 82, 83, 84, 82, 83, 84, 84, 84, 82, 82, 84, 82, 84, 83, 84, 82, 82, 82, 81, 83, 83, 83, 84, 84, 82, 82, 83, 83, 83, 82, 83, 85, 83, 82, 82, 84, 82, 82, 83, 83, 83, 82, 82, 82, 83, 82, 83, 82, 84, 82, 83, 82, 83, 82, 82, 82, 84, 82, 83, 82, 82, 86, 83, 83, 82, 83, 83, 83, 82, 84, 82, 83, 81, 82, 82, 82, 82, 83, 83, 83, 82, 83, 84, 83, 82, 83, 83, 83, 82, 83, 84, 82, 82, 83, 83]
S1.ToArray 122.3 ms -> [122, 119, 119, 120, 119, 120, 120, 121, 119, 119, 122, 120, 120, 120, 122, 120, 123, 120, 120, 120, 121, 123, 120, 120, 120, 121, 120, 121, 122, 120, 123, 119, 121, 118, 121, 120, 120, 120, 119, 124, 119, 121, 119, 120, 120, 120, 120, 120, 122, 121, 123, 230, 203, 123, 119, 119, 122, 119, 120, 120, 120, 122, 120, 121, 120, 121, 120, 121, 120, 121, 120, 120, 120, 121, 122, 121, 123, 119, 119, 119, 119, 121, 120, 120, 120, 122, 121, 122, 119, 120, 120, 121, 121, 120, 121, 120, 121, 118, 118, 118]

C2.ToList 3.43 ms -> [5, 3, 4, 4, 4, 3, 4, 4, 4, 4, 4, 3, 3, 3, 4, 3, 3, 3, 4, 3, 4, 3, 4, 3, 4, 4, 3, 3, 3, 3, 4, 3, 3, 3, 4, 3, 4, 3, 3, 3, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 3, 3, 6, 4, 4, 3, 3, 4, 3, 3, 4, 3, 3, 3, 3, 3, 3, 3, 4, 3, 3, 3, 3, 3, 3, 4, 3, 4, 4, 4, 3, 4, 4, 3, 4, 4, 4, 4, 4, 4, 3, 3, 3, 4, 4, 3, 3, 3, 3]
C2.ToArray 3.48 ms -> [3, 3, 3, 3, 4, 4, 3, 4, 4, 4, 3, 4, 3, 3, 4, 3, 3, 4, 3, 4, 3, 3, 3, 4, 3, 3, 3, 4, 3, 3, 3, 3, 4, 3, 4, 3, 3, 4, 3, 4, 3, 3, 3, 3, 4, 3, 3, 3, 3, 4, 3, 4, 3, 3, 4, 3, 3, 4, 3, 3, 4, 4, 3, 3, 4, 3, 4, 4, 3, 4, 4, 4, 4, 4, 3, 3, 3, 4, 4, 3, 4, 4, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 4, 4, 4, 4, 3]
S2.ToList 41.47 ms -> [41, 41, 49, 67, 82, 41, 41, 40, 40, 40, 40, 40, 41, 40, 40, 40, 40, 40, 41, 40, 42, 42, 40, 40, 41, 41, 41, 40, 41, 40, 41, 40, 41, 40, 42, 41, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 41, 41, 41, 41, 41, 42, 41, 41, 41, 42, 40, 41, 40, 40, 40, 42, 40, 41, 42, 41, 42, 41, 42, 40, 41, 41, 41, 41, 41, 41, 41, 41, 40, 41, 40, 41, 41, 41, 40, 41, 41, 40, 40, 41, 41, 41, 41, 41, 43, 40, 40, 41, 42, 41]
S2.ToArray 40.62 ms -> [42, 41, 44, 40, 40, 40, 40, 41, 41, 40, 41, 41, 41, 40, 41, 41, 40, 41, 41, 40, 41, 40, 40, 41, 42, 41, 41, 41, 40, 40, 40, 40, 40, 41, 41, 42, 40, 41, 41, 41, 41, 41, 40, 42, 40, 40, 41, 41, 41, 40, 41, 40, 40, 40, 40, 40, 41, 40, 40, 41, 40, 40, 40, 40, 41, 40, 41, 41, 41, 40, 41, 41, 40, 41, 40, 41, 42, 40, 41, 41, 42, 41, 41, 40, 41, 40, 41, 40, 41, 41, 40, 40, 40, 41, 41, 40, 40, 40, 40, 40]

Do giới hạn của stackoverflow đối với số lượng ký tự của câu trả lời, các danh sách mẫu của Nhóm2 và Nhóm 3 bị bỏ qua.

Như bạn có thể thấy, nó thực sự không quan trọng để sử dụng ToListhoặc ToArrytrong hầu hết các trường hợp.

Trong khi xử lý IEnumerable<T>các đối tượng được tính toán thời gian chạy , nếu tải do tính toán mang lại nặng hơn so với cấp phát bộ nhớ và các hoạt động sao chép của ToListToArray, thì sự chênh lệch là không đáng kể ( C.ToList vs C.ToArrayS.ToList vs S.ToArray).

Sự khác biệt chỉ có thể được quan sát trên các IEnumerable<T>đối tượng chỉ tính toán không phải thời gian chạy ( C1.ToList vs C1.ToArrayS1.ToList vs S1.ToArray). Nhưng sự khác biệt tuyệt đối (<60ms) vẫn được chấp nhận trên một triệu vật thể nhỏ IEnumerable<T>. Trong thực tế, sự khác biệt được quyết định bởi việc thực hiện Enumerator<T>của IEnumerable<T>. Vì vậy, nếu chương trình của bạn thực sự thực sự nhạy cảm về vấn đề này, bạn phải lập hồ sơ, hồ sơ, hồ sơ ! Cuối cùng, bạn có thể thấy rằng nút cổ chai không phải trên ToListhoặc ToArray, mà là chi tiết của các điều tra viên.

Và, kết quả C2.ToList vs C2.ToArrayS2.ToList vs S2.ToArraycho thấy rằng, bạn thực sự không cần phải quan tâm ToListhoặc ToArraytrên các ICollection<T>đối tượng không tính toán thời gian chạy .

Tất nhiên, đây chỉ là kết quả trên máy của tôi, thời gian thực tế của các thao tác này trên các máy khác nhau sẽ không giống nhau, bạn có thể tìm hiểu trên máy của mình bằng mã ở trên.

Lý do duy nhất bạn cần đưa ra lựa chọn là, bạn có nhu cầu cụ thể về List<T>hoặc T[], như được mô tả bởi câu trả lời của @Jeppe Stig Nielsen .


1

Đối với bất kỳ ai quan tâm đến việc sử dụng kết quả này trong một Linq-to-sql khác, chẳng hạn như

from q in context.MyTable
where myListOrArray.Contains(q.someID)
select q;

thì SQL được tạo giống nhau cho dù bạn đã sử dụng Danh sách hay Mảng cho myListOrArray. Bây giờ tôi biết một số người có thể hỏi tại sao thậm chí liệt kê trước câu lệnh này, nhưng có một sự khác biệt giữa SQL được tạo từ IQueryable vs (List hoặc Array).

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.