2013-05-31 43 views
6

Đây là lần đầu tiên tôi ở đây và tôi đang đấu tranh để giải quyết vấn đề này. Tôi có đoạn mã này:C# ListView.Items [i] .remove là rất chậm

try 
{ 
    progressBar1.Maximum = lista.Items.Count; 
    lista.BeginUpdate(); 

    for (int i = 0; lista.Items.Count > i; i++) 

    //for (int i = lista.Items.Count - 1; -1 < i; i--) 
    { 
     if (lista.Items[i].SubItems[1].Text.ToLower().Contains(Text) == false) 
     {       
      lista.Items[i].Remove();       
     } 

     progressBar1.Value = progressBar1.Value + 1; 
    } 

    lista.EndUpdate(); 

    progressBar1.Value = 0; 
} 
catch (Exception errore) 
{ 
    txt_info.Text = "" + errore.Message; 
    progressBar1.Value = 0; 
} 

Phương pháp lista.items[i].remove là cực kỳ chậm. listaListView và tôi đang làm việc trên tệp nhật ký lớn hơn 50.000 dòng. Có cách nào để tăng tốc quá trình không?

+3

Tò mò ... có 'lista.Items.RemoveAt (i)' khác về tốc độ không? Có lẽ (phản trực giác) lớp phải quay lại và giải quyết chính chỉ mục đó. – DonBoitnott

+4

Bạn không nên thay đổi kích thước của cấu trúc dữ liệu (loại bỏ các mục khỏi nó) trong vòng lặp For. Hãy thử viết lại vòng lặp mà không cần xóa nó. Ví dụ, đánh dấu các chỉ mục cần được loại bỏ trong vòng lặp for và sau đó loại bỏ chúng ra bên ngoài nó. –

+2

@RezaShirazian Một cách rõ ràng không thể làm điều đó với 'foreach' ... Tôi tin rằng nó hợp lý ok để sử dụng' for' để loại bỏ các mục (cuối cùng một số đoạn mã phải làm điều đó anyway) - cũng rõ ràng là không chính xác bằng văn bản (như trong mẫu ở trên do bỏ qua các mục bên cạnh mục vừa xóa) là ý tưởng tồi. –

Trả lời

1
ListViewItem[] allElements = new ListViewItem[listView1.Items.Count]; 
listView1.Items.CopyTo(allElements, 0); 
List <ListViewItem> list = allElements.ToList(); 
list.RemoveAll(item => item.SubItems[1].Text.ToLower().Contains(TextToFind) == false); 
listView1.BeginUpdate(); 
listView1.Clear(); 
listView1.Items.AddRange(list.ToArray()); 
listView1.EndUpdate(); 

Quy tắc đầu tiên không bao giờ được cập nhật danh sách trong vòng lặp. Logic của bạn sẽ chỉ chạy đến một nửa danh sách. Tôi đoán đó không phải là những gì bạn muốn.
Tôi đã thấy rằng thao tác listview.items rất chậm ngay cả sau khi sử dụng BeginUpdate và EndUpdate. Điều quan trọng là thực hiện thao tác bên ngoài (trong danh sách hoặc hơn) và sau đó điền lại danh sách với AddRange (nhanh hơn Add).

+0

tôi đã thử mã ở trên nhưng cho tôi trở lại một danh sách trống. Tôi có nhớ gì không Trong bình luận của bạn, bạn nói rằng bạn đang sử dụng vòng lặp foreach nhưng tôi không nhìn thấy nó trong mã mà bạn để lại. – Jarlaxle2k5

+0

@ user2441083: Nhận xét cũ hơn của tôi đã sử dụng foreach và sau đó tôi đã xóa các phần tử khỏi danh sách. Nhưng sau đó tôi sử dụng RemoveAll mà không yêu cầu foreach. Btw, khi nào danh sách trở nên trống? Sau khi RemoveAll? nếu có, bạn cần phải kiểm tra xem điều kiện được viết trong "RemoveAll" có thể đang hoạt động không. – Yogee

+0

@ user2441083: Tôi đã viết cùng một logic trong ứng dụng của tôi và nó đang làm việc tốt và đủ nhanh .. mất khoảng 2450 mili giây để loại bỏ 10% thời gian danh sách cho 50.000 mục của chuỗi hai chiều như LIstViewItems trong listView1.Items. Bạn có chắc chắn muốn kiểm tra mục.SubItems [1] chứ không phải mục.SubItems [0]? – Yogee

3

tôi sẽ có một cách tiếp cận khác nhau và sử dụng LINQ, một cái gì đó như thế này:

lista.Items = lista.Items.Where(x=>x.SubItems[1].Text.ToLower.Contains(Text)).AsParallel().ToList(); 

Về cơ bản, xây dựng lại danh sách một lần chứ không phải cố gắng để loại bỏ các mục cá nhân hơn và hơn nữa.

+1

Đây là một triển khai tốt và nhỏ gọn, mặc dù AsParallel tấn công tôi là quá mức cần thiết. –

+0

ToLower và Contains là cả hai CPU hoạt động chi phí, nếu danh sách lớn nó sẽ được hưởng lợi từ cách tiếp cận song song –

+2

['ListView.Items'] (http://msdn.microsoft.com/en-us/library/system. windows.forms.listview.items.aspx) là một thuộc tính chỉ đọc, do đó, điều này không hoạt động. –

3

Tùy chọn đơn giản nhất là sử dụng danh sách RemoveAll method riêng của danh sách.

list.RemoveAll(x => !x.SubItems[1].Text.ToLower().Contains(Text))

P.S.

Bạn có thể muốn tìm kiếm tốc độ tăng trong so sánh thực tế. Sử dụng String.Compare nhanh hơn nhiều nếu yêu cầu của bạn phù hợp với nó. Nếu bạn muốn kiểm tra chuỗi phụ, tôi khuyên bạn nên sử dụng ToUpperInvariant cho các vấn đề liên quan đến bất biến - it's designed to be faster.

+0

['ListViewItemCollection'] (http://msdn.microsoft.com/en-us/library/system.windows.forms.listview.listviewitemcollection.aspx) không có phương thức 'RemoveAll', vì vậy nó không hoạt động. –

+0

@Yogee Tôi đã đề xuất rằng lọc nhanh hơn vài lần để so sánh trực tiếp - không phải thay thế cho 'Chứa'. Nếu anh ta muốn sử dụng 'Contains', thì' ToUpperInvariant' nhanh hơn. – Asti

+0

@Yogee SO cũng có thể là một nơi để tư vấn chung. Không chỉ "cho tôi mã số". Tuy nhiên, tôi đã chỉnh sửa nó để loại bỏ sự mơ hồ. – Asti

0

Bạn có thể dán nó vào một nhân viên nền và tự mình làm điều này. Do đó người dùng của bạn vẫn có thể sử dụng chương trình trong khi quá trình này diễn ra.