2010-12-31 18 views

Trả lời

18

ToArray không nhất thiết phải nhanh hơn ToList. Chỉ cần sử dụng ToList.

Điểm là miễn là bạn không biết số lượng phần tử của chuỗi gốc trước khi liệt kê, bạn kết thúc với việc thay đổi kích thước mảng và thêm các phần tử vào nó như List<T>, vì vậy ToArray sẽ phải thực hiện cùng một điều List<T> nào. Bên cạnh đó, ToList cung cấp cho bạn List<T> và đẹp hơn một mảng thô.

Tất nhiên, nếu bạn biết loại bê tông của thể hiện IEnumerable<T>, có thể có các phương pháp nhanh hơn, nhưng đó không phải là nguyên nhân cho vấn đề.

Lưu ý phụ: sử dụng mảng (trừ khi bạn phải) được cho là tối ưu hóa vi mô và phải là avoidedmost of the time.

+0

Để lưu ý chúng hầu như tương đương. – Vadim

+0

@Yads: chính xác. –

+1

+1. Trong thực tế, Mono ['Enumerable.ToArray'] (https://github.com/mono/mono/blob/master/mcs/class/System.Core/System.Linq/Enumerable.cs#L2794) sử dụng' Danh sách mới (nguồn) .ToArray() ', trừ khi' IEnumerable 'xảy ra là' ICollection '. Tôi sẽ không ngạc nhiên nếu MS là tương tự. –

2

Cách thứ hai đến rẻ nhất là nói new List<T>(myEnumerable).ToArray(). Cách rẻ nhất là sử dụng .ToArray() (từ LINQ) hoặc, nếu bạn không có C# 3.5, để tạo bộ đệm của riêng bạn và thêm vào nó trong khi tăng gấp đôi kích thước của nó, sau đó cắt nó ở cuối.

5

Enumerable::ToArrayEnumerable::ToList cuối cùng sử dụng kỹ thuật tương tự để nhận các phần tử từ nguồn vào bộ đệm mảng bên trong, chúng sẽ phân bổ bộ đệm mới tăng gấp đôi kích thước, ghi nhớ và tiếp tục thêm các phần tử, lặp lại quá trình này cho đến khi liệt kê nguồn hoàn tất. Sự khác biệt cuối cùng là ToArray, sử dụng triển khai thực hiện Buffer<T> trong nội bộ, sau đó phải phân bổ chính xác kích thước Array và sao chép các phần tử vào đó trước khi trả lại kết quả. Mặt khác, ToList chỉ cần trả lại List<T> với bộ đệm mảng có khả năng được tạo một phần (có khả năng) bên trong nó.

Cả hai hiện thực cũng có một tối ưu hóa mà nếu nguồn IEnumerable là một ICollection họ sẽ thực sự phân bổ chính xác kích thước bộ đệm ngay để bắt đầu với việc sử dụng ICollection::Count và sau đó sử dụng ICollection::CopyTo từ nguồn để điền vào bộ đệm của họ. Cuối cùng, bạn sẽ thấy rằng chúng hoạt động gần như giống hệt nhau trong hầu hết các trường hợp, nhưng List<T> về mặt kỹ thuật là lớp "nặng hơn" để treo vào cuối và ToArray có thêm phân bổ + memcpy ở cuối (nếu nguồn không phải là ICollection) để có thể quay lại đúng mảng có kích thước chính xác. Tôi thường gắn bó với ToList bản thân mình trừ khi tôi biết tôi cần phải chuyển kết quả đến thứ gì đó yêu cầu một mảng như nói có thể Task::WaitAll.

4

Tôi sắp đề xuất khả năng sử dụng .AsParallel().ToList() nếu bạn có TPL theo ý của mình, nhưng thử nghiệm không chính thức trên máy tính xách tay lõi kép của tôi cho thấy nó chậm hơn 7 lần so với chỉ .ToList(). Vì vậy, hãy liên kết với Mehrdad'sanswer.

+1

+1 Đó có khả năng là một lựa chọn tốt, và tùy thuộc vào bản chất của đối tượng người nhận, nó có thể nhanh hơn rất nhiều, nhưng điều đó rơi vào danh mục "không phải là germane đến điểm". –

0

Tôi biết điều này khá cũ ...

Tại sao bạn không thể làm điều gì đó như thế này ...

IEnumerable <T> original = {... chèn mã vào đây để điền ...};

IEnumerable <T> copy = (từ hàng trong hàng chọn ban đầu);

Tôi đã sử dụng phương pháp này trước khi trì hoãn việc tải bản sao và các phần tử của nó cho đến khi tôi thực sự cần chúng, cộng với, tôi không thay đổi kiểu dữ liệu cơ bản của "gốc" thành kiểu danh sách.

+0

Chọn danh tính không thực sự mang lại cho bạn bất kỳ điều gì ở đây. Điều này không thực sự có chức năng khác với việc chỉ sao chép tham chiếu đến 'IEnumerable' khác, ngoài việc này thêm một chút chi phí phụ trội vào thời gian chạy và ngăn cản việc truyền đến kiểu cơ bản. Nếu bạn muốn có một ảnh chụp nhanh về những gì mà trình tự trông giống tại một thời điểm 'ToList' là phương pháp đúng đắn. Nếu bạn không, và bạn muốn một chuỗi được tạo ra trong cùng một trang viên giống như trình tự khác, chỉ cần sao chép tham chiếu chính nó; không cần phải làm gì nữa. – Servy

Các vấn đề liên quan