2011-07-13 22 views
6

Tôi có hai bộ sưu tậpC# cách hiệu quả hơn so với hai bộ sưu tập

List<Car> currentCars = GetCurrentCars(); 
List<Car> newCars = GetNewCars(); 

Tôi không muốn sử dụng vòng lặp foreach hoặc một cái gì đó bởi vì tôi nghĩ rằng cần có cách tốt hơn để làm điều này.

Tôi đang tìm cách hiệu quả hơn để so sánh bộ sưu tập này và để có được kết quả:

  1. Danh sách xe hơi mà là trong newCars và không có trong currentCars
  2. Danh sách xe mà không phải là trong newCars và trong currentCars

Loại ô tô có thuộc tính int Id.

Có được một câu trả lời, mà đã được xóa nói gì tôi có nghĩa là bằng cách nói hiệu quả: mã ít hơn, cơ khí ít hơn, và các trường hợp dễ đọc hơn

Vì vậy, suy nghĩ theo cách này các trường hợp tôi có là gì?

Điều gì sẽ ít mã, ít cơ học hơn và các trường hợp dễ đọc hơn?

+1

Các hoạt động bạn mô tả là "sự khác biệt được đặt". Nếu bạn cần phải tính toán sự khác biệt thiết lập rất nhiều, hoặc trên bộ lớn, sau đó bạn không nên sử dụng Danh sách ở nơi đầu tiên. Bạn nên sử dụng HashSet , một cấu trúc dữ liệu được tối ưu hóa đặc biệt để tính toán các loại khác biệt này. –

+0

@Eric Lippert trong trường hợp của tôi nó là bộ rất nhỏ (1-3 mục), vì vậy tôi nghĩ rằng không có nhu cầu trong HashSet . – Joper

Trả lời

10

Bạn có thể sử dụng Except:

var currentCarsNotInNewCars = currentCars.Except(newCars); 
var newCarsNotInCurrentCars = newCars.Except(currentCars); 

Nhưng điều này không có lợi ích hiệu suất so với giải pháp foreach. Nó chỉ trông sạch hơn.
Ngoài ra, hãy lưu ý rằng bạn cần triển khai IEquatable<T> cho lớp học Car của mình, vì vậy việc so sánh được thực hiện trên ID chứ không phải trên tham chiếu.

Performancewise, một cách tiếp cận tốt hơn sẽ không sử dụng một List<T> nhưng một Dictionary<TKey, TValue> với ID là chìa khóa:

var currentCarsDictionary = currentCars.ToDictionary(x => x.ID); 
var newCarsDictionary = newCars.ToDictionary(x => x.ID); 

var currentCarsNotInNewCars = 
    currentCarsDictionary.Where(x => !newCarsDictionary.ContainsKey(x.Key)) 
         .Select(x => x.Value); 

var newCarsNotInCurrentCars = 
    newCarsDictionary.Where(x => !currentCarsDictionary.ContainsKey(x.Key)) 
        .Select(x => x.Value); 
+1

Nhưng đây thực chất là một vòng lặp – landoncz

+0

@landoncz: Tôi biết. Tôi nghĩ rằng nó không thể không sử dụng một vòng lặp. –

+0

Tôi đồng ý, về mặt kỹ thuật bạn phải sử dụng một vòng lặp trừ khi bạn sẽ làm một số đệ quy điên (sau đó bạn sẽ có một cái gì đó không thể duy trì và cũng có nguy cơ thổi ngăn xếp). –

12

Bạn có thể làm điều đó như thế này:

// 1) List of cars in newCars and not in currentCars 
var newButNotCurrentCars = newCars.Except(currentCars); 

// 2) List of cars in currentCars and not in newCars 
var currentButNotNewCars = currentCars.Except(newCars); 

Việc sử dụng đang phương thức mở rộng Enumerable.Except (có sẵn trong .Net 3.5 trở lên).

Tôi tin rằng điều này đáp ứng tiêu chí của bạn về "ít mã, ít cơ học hơn và dễ đọc hơn".

+0

+1, LINQ có lẽ là một cách dễ dàng, đặc biệt với [Ngoại trừ()] (http://msdn.microsoft.com/en-us/library/system.linq.enumerable.except.aspx). –

+1

+1 nhưng không có bảo đảm rằng ngoại trừ sẽ thực hiện một cho mỗi vì nó rất có thể sẽ sử dụng Ienumerable và foreach bất kỳ cách nào. – rerun

+0

LINQ = vòng lặp, mà ông nói rằng ông không muốn làm ... – landoncz

2

Tôi sẽ ghi đè số Equals của số Car để so sánh theo id và sau đó bạn có thể sử dụng phương thức mở rộng IEnumerable.Except. Nếu bạn không thể ghi đè số Equals, bạn có thể tạo IEqualityComparer<Car> của riêng mình so sánh hai xe bằng id.

class CarComparer : IEqualityComparer<Car> 
{ 
    public bool Equals(Car x, Car y) 
    { 
     return x != null && y != null && x.Id == y.Id; 
    } 

    public int GetHashCode(Car obj) 
    { 
     return obj == null ? 0 : obj.Id; 
    } 
} 
+0

Tôi nghĩ rằng đây là cách duy nhất để làm điều đó mà không lặp qua từng mục trong một số thời trang. – landoncz

+0

@landoncz: Tôi không thấy cách này tránh vòng lặp. Ông cũng đề xuất sử dụng phương pháp mở rộng 'Trừ', mặc dù ông đã viết sai chính tả. –

+0

Xin lỗi, tôi đã nghĩ anh ấy đã ghi đè toàn bộ danh sách như thế này, D'oh, bạn không thể thêm mã vào thư trả lời ... – landoncz

2

Bạn có thể sử dụng LINQ ...

 List<Car> currentCars = new List<Car>(); 
     List<Car> newCars = new List<Car>(); 

     List<Car> currentButNotNew = currentCars.Where(c => !newCars.Contains(c)).ToList(); 
     List<Car> newButNotCurrent = newCars.Where(c => !currentCars.Contains(c)).ToList(); 

... nhưng đừng để bị lừa.Nó có thể ít mã cho bạn, nhưng chắc chắn sẽ có một số cho vòng ở đâu đó

EDIT: Đã không nhận ra có một phương pháp Trừ :(

3

Nếu bạn bắt đầu với họ trong HashSet s bạn có thể sử dụng phương pháp Except.

HashSet<Car> currentCars = GetCurrentCars(); 
HashSet<Car> newCars = GetNewCars(); 

currentCars.Except(newCars); 
newCars.Except(currentCars); 

Nó sẽ là nhanh hơn nhiều w/một tập hơn một danh sách. (Dưới mui xe một danh sách chỉ được làm một foreach, bộ có thể được tối ưu hóa).

+1

+1 cho HashSet. Mặc dù nó sẽ thực hiện nhanh hơn nó sẽ tiêu thụ nhiều bộ nhớ hơn và chi phí ban đầu của populating HashSet sẽ cao hơn. Bạn chỉ nên sử dụng HashSet nếu bạn định sử dụng cùng một bộ sưu tập nhiều lần. –

+0

Rất đúng. Sự cân bằng gần như luôn luôn là kích thước cho tốc độ, mặc dù. – Crisfole

1

Nếu bạn' đang tìm kiếm hiệu quả, triển khai IComparable trên Ô tô (s) orting trên ID duy nhất của bạn) và sử dụng một SortedList. Sau đó, bạn có thể đi qua các bộ sưu tập của mình với nhau và đánh giá các kiểm tra của bạn trong O (n). Điều này tất nhiên đi kèm với một chi phí thêm vào danh sách chèn để duy trì bản chất được sắp xếp.

0

Bạn có thể sao chép danh sách nhỏ hơn vào bộ sưu tập dựa trên bảng băm như HashSet hoặc Dictionary và sau đó lặp qua danh sách thứ hai và kiểm tra xem mục có tồn tại trong bảng băm hay không.

điều này sẽ giảm thời gian từ O (N^2) trong trường hợp ngây thơ trong trường hợp foreach thành O (N). Đây là cách tốt nhất bạn có thể làm mà không cần biết thêm về các danh sách (bạn có thể làm ít tốt hơn nếu danh sách được sắp xếp chẳng hạn, nhưng vì bạn phải "chạm" từng ô tô ít nhất là một lần để kiểm tra xem nó có nằm trong danh sách xe mới, bạn không bao giờ có thể làm tốt hơn O (N))

0

Nếu so sánh thuộc tính Id sẽ đủ để bạn nói nếu Ô tô tương đương với xe khác, để tránh một số loại vòng lặp, bạn có thể ghi đè Danh sách với lớp của riêng bạn theo dõi các mục và sử dụng IEqualityComparer trên toàn bộ bộ sưu tập, như sau:

class CarComparer : IList<Car>, IEquatable<CarComparer> 
{ 
    public bool Equals(CarComparer other) 
    { 
     return object.Equals(GetHashCode(),other.GetHashCode()); 
    } 

    public override int GetHashCode() 
    { 
     return _runningHash; 
    } 

    public void Insert(int index, Car item) 
    { 
     // Update _runningHash here 
     throw new NotImplementedException(); 
    } 

    public void RemoveAt(int index) 
    { 
     // Update _runningHash here 
     throw new NotImplementedException(); 
    } 

    // More IList<Car> Overrides .... 
} 

Sau đó, bạn chỉ cần ghi đè Add, Remove, v.v ... và bất kỳ phương pháp nào khác có thể ảnh hưởng đến các mục trong danh sách. Sau đó, bạn có thể giữ một biến riêng tư là một băm của một số loại Id của các mục trong danh sách. Khi ghi đè các phương thức Equals của bạn, bạn có thể chỉ so sánh biến riêng tư này. Không phải là cách tiếp cận sạch nhất cho đến nay (vì bạn phải theo kịp với biến băm của bạn), nhưng nó sẽ dẫn đến việc bạn không phải lặp lại để so sánh. Nếu đó là tôi, tôi sẽ chỉ sử dụng LINQ như một số đã đề cập ở đây ...

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