Danh sách <T> có đảm bảo thứ tự chèn không?


238

Giả sử tôi có 3 chuỗi trong Danh sách (ví dụ: "1", "2", "3").

Sau đó, tôi muốn sắp xếp lại chúng để đặt "2" ở vị trí 1 (ví dụ "2", "1", "3").

Tôi đang sử dụng mã này (đặt indexToMoveTo thành 1):

listInstance.Remove(itemToMove);
listInstance.Insert(indexToMoveTo, itemToMove);

Điều này có vẻ hiệu quả, nhưng tôi thỉnh thoảng nhận được kết quả lạ; đôi khi thứ tự không chính xác hoặc các mục từ danh sách đang bị xóa!

Có ý kiến ​​gì không? Có List<T>đảm bảo trật tự?

Liên quan:

Danh sách <T> có đảm bảo rằng các mục sẽ được trả lại theo thứ tự chúng đã được thêm không?


3
Điều này không đúng như đã đề cập ở đây: stackoverflow.com/a/1790318/696517
jrmgx

Câu trả lời:


311

Các List<>lớp thực hiện đặt hàng đảm bảo - điều sẽ được giữ lại trong danh sách theo thứ tự bạn thêm chúng, trong đó có bản sao, trừ khi bạn rõ ràng sắp xếp danh sách.

Theo MSDN:

... Danh sách "Đại diện cho một danh sách các đối tượng được gõ mạnh có thể được truy cập bởi chỉ mục ."

Các giá trị chỉ số phải vẫn đáng tin cậy để điều này là chính xác. Do đó, trật tự được đảm bảo.

Bạn có thể nhận được kết quả kỳ lạ từ mã của mình nếu bạn di chuyển mục sau trong danh sách, vì bạn Remove()sẽ di chuyển tất cả các mục khác xuống một nơi trước khi gọi đến Insert().

Bạn có thể đun mã của bạn xuống một cái gì đó đủ nhỏ để đăng?


63
Đối với bất kỳ nhân viên nào trong tương lai, đây là trích dẫn chính xác từ MSDN (bashing mine) cho Danh sách (T) .Thêm Đối tượng được thêm vào cuối Danh sách <T>. Giá trị có thể là null cho các loại tham chiếu.
aolszowka

4
Có một trích dẫn / tài liệu tham khảo chính xác hơn mà chúng tôi có thể nhận được từ Microsoft hoặc đặc tả C # về điều này không? Trích dẫn của @ aolszowka dường như chắc chắn gợi ý rằng nó vẫn giữ thứ tự chèn, nhưng về mặt kỹ thuật, Danh sách có thể sắp xếp lại bộ sưu tập bất cứ lúc nào sau khi một mục được thêm vào và tuyên bố đó vẫn còn hiệu lực. Tôi không muốn nhận được sự kén chọn về nó, nhưng nếu một người quản lý hoặc QA thực sự khiến tôi bảo vệ vị trí này, tôi sẽ không cảm thấy rất tự tin chỉ với câu nói đó.
tehDorf

4
Tôi có thể nghĩ ra hai cách hữu ích để có được một số xác nhận. Đầu tiên, đọc nguồn và thỏa mãn chính mình. Thứ hai, kiểm tra định nghĩa của loại dữ liệu trừu tượng Listtrong bất kỳ sách giáo khoa khoa học máy tính tốt. Cũng giống như QueueStack, a Listlà một cấu trúc dữ liệu được xác định rõ ràng, có thể dự đoán và được hiểu rõ - nếu việc triển khai .NET khác nhau (hoặc nếu nó thay đổi), rất nhiều phần mềm sẽ bị hỏng .
Bevan

@tehDorf Tôi chấp nhận điều này (nếu có một cuộc thảo luận về nó) là: "Nếu việc đặt hàng ảnh hưởng đến hoạt động của phương thức / thuật toán của bạn, bạn nên đặt hàng danh sách một cách rõ ràng hoặc yêu cầu API của bạn lấy Giao diện / lớp thích hợp như IOrderedEnumerable hoặc SortedList, nếu không, bạn không nên dựa vào một triển khai cụ thể để hành xử theo một cách nhất định trừ khi nó được tuyên bố rõ ràng một cách rõ ràng "Bạn cũng phải cẩn thận về việc sắp xếp Danh sách <T> một cách rõ ràng vì việc triển khai của chúng sử dụng một loại không ổn định.
aolszowka 18/07/2015

1
@achuthakrishnan Chúng là những vấn đề riêng biệt. Danh sách đảm bảo rằng các mục được giữ lại theo thứ tự chúng được thêm vào danh sách. Khi bạn sử dụng Where()phương pháp LINQ để lọc danh sách, bạn đang dựa vào việc triển khai để xem xét các mục theo thứ tự. Nó xảy ra như vậy (xem Referenceource.microsoft.com/System.Core/System/Linq/ mẹo ), nhưng điều này không được ghi lại trong MSDN. Tôi khuyên bạn nên sử dụng emp.LastOrDefault(x => x.empDept = 'abc')làm tài liệu LastOrDefault()cho biết nó duy trì thứ tự của các mặt hàng.
Bevan

36

Dưới đây là 4 mục, với chỉ mục của họ

0  1  2  3
K  C  A  E

Bạn muốn di chuyển K đến giữa A và E - bạn có thể nghĩ vị trí 3. Bạn phải cẩn thận về việc lập chỉ mục của mình ở đây, vì sau khi xóa, tất cả các chỉ mục đều được cập nhật.

Vì vậy, bạn loại bỏ mục 0 trước, để lại

0  1  2
C  A  E

Sau đó, bạn chèn vào 3

0  1  2  3
C  A  E  K

Để có kết quả chính xác, bạn nên sử dụng chỉ mục 2. Để làm cho mọi thứ nhất quán, bạn sẽ cần gửi đến (indexToMoveTo-1) if indexToMoveTo > indexToMove, ví dụ:

bool moveUp = (listInstance.IndexOf(itemToMoveTo) > indexToMove);
listInstance.Remove(itemToMove);
listInstance.Insert(indexToMoveTo, moveUp ? (itemToMoveTo - 1) : itemToMoveTo);

Điều này có thể liên quan đến vấn đề của bạn. Lưu ý mã của tôi chưa được kiểm tra!

EDIT : Ngoài ra, bạn có thể Sortvới một bộ so sánh tùy chỉnh ( IComparer) nếu phù hợp với tình huống của bạn.


Không đọc phản hồi của Bevan đủ cẩn thận, tôi chỉ che đậy mặt đất anh ta có.
Joel Goodwin

9
Vâng, nhưng bạn đã xây dựng và đưa ra một ví dụ về câu trả lời của Bevan, cũng như một số mã giải pháp, vì vậy câu trả lời của bạn không mang tính thời sự và tôi đã bình chọn cho bạn.
Jason S

9

Như Bevan đã nói, nhưng hãy nhớ rằng, chỉ mục danh sách là dựa trên 0. Nếu bạn muốn di chuyển một phần tử ra phía trước danh sách, bạn phải chèn nó vào chỉ mục 0 (không phải 1 như trong ví dụ của bạn).


1

Đây là mã tôi có để di chuyển một mục xuống một nơi trong danh sách:

if (this.folderImages.SelectedIndex > -1 && this.folderImages.SelectedIndex < this.folderImages.Items.Count - 1)
{
    string imageName = this.folderImages.SelectedItem as string;
    int index = this.folderImages.SelectedIndex;

    this.folderImages.Items.RemoveAt(index);
    this.folderImages.Items.Insert(index + 1, imageName);
    this.folderImages.SelectedIndex = index + 1;
 }

và điều này để di chuyển nó lên một nơi:

if (this.folderImages.SelectedIndex > 0)
{
    string imageName = this.folderImages.SelectedItem as string;
    int index = this.folderImages.SelectedIndex;

    this.folderImages.Items.RemoveAt(index);
    this.folderImages.Items.Insert(index - 1, imageName);
    this.folderImages.SelectedIndex = index - 1;
}

folderImageslà một ListBoxkhóa học vì vậy danh sách này là a ListBox.ObjectCollection, không phải a List<T>, nhưng nó được thừa hưởng từ IListđó nên nó sẽ hoạt động giống nhau. Không giúp đỡ à?

Tất nhiên cái trước chỉ hoạt động nếu mục được chọn không phải là mục cuối cùng trong danh sách và mục sau nếu mục được chọn không phải là mục đầu tiên.


1

Nếu bạn sẽ thay đổi thứ tự các thao tác, bạn sẽ tránh được hành vi lạ: Đầu tiên hãy chèn giá trị vào đúng vị trí trong danh sách, sau đó xóa nó khỏi vị trí đầu tiên. Hãy chắc chắn rằng bạn xóa nó theo chỉ mục của anh ấy, bởi vì nếu bạn sẽ xóa nó bằng tham chiếu, bạn có thể xóa cả hai ...

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.