2008-08-22 23 views
39

Tôi có hai bộ sưu tập của cùng một đối tượng, Collection<Foo> oldSetCollection<Foo> newSet. Logic yêu cầu là như sau:Cách tốt nhất để so sánh hai bộ sưu tập trong Java và hành động trên chúng?

  • nếu foo là trong (*) oldSet nhưng không newSet, gọi doRemove(foo)
  • else if foo không có trong oldSet nhưng trong newSet, gọi doAdd(foo)
  • else if foo là trong cả hai bộ sưu tập nhưng được sửa đổi, hãy gọi doUpdate(oldFoo, newFoo)
  • khác nếu !foo.activated && foo.startDate >= now, gọi doStart(foo)
  • khác nếu foo.activated && foo.endDate <= now, hãy gọi doEnd(foo)

(*) "trong" nghĩa là số nhận dạng duy nhất khớp, không nhất thiết là nội dung.

Dòng điện (di sản) mã không nhiều so sánh để tìm ra removeSet, addSet, updateSet, startSetendSet, và sau đó vòng lặp để hoạt động trên từng hạng mục.

Mã này khá lộn xộn (một phần vì tôi đã loại bỏ một số logic spaghetti) và tôi đang cố gắng tái cấu trúc nó. Một số thông tin nền hơn:

  • Theo như tôi biết, oldSetnewSet đang thực sự hậu thuẫn của ArrayList
  • Mỗi bộ chứa ít hơn 100 mặt hàng, nhiều khả năng tối đa hiện tại 20
  • Mã này được gọi là thường xuyên (đo bằng triệu/ngày), mặc dù các bộ hiếm khi khác

câu hỏi của tôi:

  • Nếu tôi chuyển đổi oldSetnewSet thành HashMap<Foo> (thứ tự không phải là mối quan tâm ở đây), với các ID là khóa, nó sẽ làm cho mã dễ đọc hơn và dễ dàng hơn để so sánh? Mất bao nhiêu thời gian & hiệu năng bộ nhớ bị mất trên chuyển đổi?
  • Lặp lại hai bộ và thực hiện thao tác thích hợp sẽ hiệu quả hơn và súc tích hơn?

Trả lời

-1

Đối với một tập hợp nhỏ thường không có giá trị để chuyển đổi từ Mảng thành HashMap/bộ. Trong thực tế, bạn có thể tốt nhất để giữ chúng trong một mảng và sau đó phân loại chúng theo khóa và lặp lại trên cả hai danh sách cùng một lúc để làm so sánh.

9

Tôi đã tạo gần đúng những gì tôi nghĩ bạn đang tìm kiếm chỉ bằng cách sử dụng Khung sưu tập trong Java. Thành thật mà nói, tôi nghĩ rằng nó có lẽ là quá mức cần thiết như @ Boong Deck chỉ ra. Đối với một tập hợp nhỏ các mục để so sánh và xử lý, tôi nghĩ mảng sẽ là một lựa chọn tốt hơn từ quan điểm thủ tục nhưng đây là giải pháp giả của tôi (vì tôi lười).Tôi có một giả định rằng các lớp Foo có thể so sánh dựa trên đó là id duy nhất và không phải tất cả các dữ liệu trong nội dung của nó:

Collection<Foo> oldSet = ...; 
Collection<Foo> newSet = ...; 

private Collection difference(Collection a, Collection b) { 
    Collection result = a.clone(); 
    result.removeAll(b) 
    return result; 
} 

private Collection intersection(Collection a, Collection b) { 
    Collection result = a.clone(); 
    result.retainAll(b) 
    return result; 
} 

public doWork() { 
    // if foo is in(*) oldSet but not newSet, call doRemove(foo) 
    Collection removed = difference(oldSet, newSet); 
    if (!removed.isEmpty()) { 
     loop removed { 
      Foo foo = removedIter.next(); 
      doRemove(foo); 
     } 
    } 
    //else if foo is not in oldSet but in newSet, call doAdd(foo) 
    Collection added = difference(newSet, oldSet); 
    if (!added.isEmpty()) { 
     loop added { 
      Foo foo = addedIter.next(); 
      doAdd(foo); 
     } 
    } 

    // else if foo is in both collections but modified, call doUpdate(oldFoo, newFoo) 
    Collection matched = intersection(oldSet, newSet); 
    Comparator comp = new Comparator() { 
     int compare(Object o1, Object o2) { 
      Foo f1, f2; 
      if (o1 instanceof Foo) f1 = (Foo)o1; 
      if (o2 instanceof Foo) f2 = (Foo)o2; 
      return f1.activated == f2.activated ? f1.startdate.compareTo(f2.startdate) == 0 ? ... : f1.startdate.compareTo(f2.startdate) : f1.activated ? 1 : 0; 
     } 

     boolean equals(Object o) { 
      // equal to this Comparator..not used 
     } 
    } 
    loop matched { 
     Foo foo = matchedIter.next(); 
     Foo oldFoo = oldSet.get(foo); 
     Foo newFoo = newSet.get(foo); 
     if (comp.compareTo(oldFoo, newFoo) != 0) { 
      doUpdate(oldFoo, newFoo); 
     } else { 
      //else if !foo.activated && foo.startDate >= now, call doStart(foo) 
      if (!foo.activated && foo.startDate >= now) doStart(foo); 

      // else if foo.activated && foo.endDate <= now, call doEnd(foo) 
      if (foo.activated && foo.endDate <= now) doEnd(foo); 
     } 
    } 
} 

Theo như câu hỏi của bạn: Nếu tôi chuyển đổi oldSet và newSet vào HashMap (thứ tự là không quan tâm ở đây), với các ID là khóa, nó sẽ làm cho mã dễ đọc hơn và dễ so sánh hơn? Mất bao nhiêu thời gian & hiệu năng bộ nhớ bị mất trên chuyển đổi? Tôi nghĩ rằng bạn có lẽ sẽ làm cho mã dễ đọc hơn bằng cách sử dụng Bản đồ NHƯNG ... bạn có thể sử dụng nhiều bộ nhớ và thời gian hơn trong quá trình chuyển đổi.

Lặp lại hai bộ và thực hiện thao tác thích hợp sẽ hiệu quả hơn và súc tích hơn? Có, đây sẽ là tốt nhất của cả hai thế giới, đặc biệt nếu bạn theo lời khuyên của @Mike Sharek về Danh sách riêng của bạn với các phương pháp chuyên ngành hoặc theo dõi một thứ gì đó giống như mẫu Thiết kế khách truy cập để chạy qua bộ sưu tập của bạn và xử lý từng mục.

2

tôi muốn chuyển sang danh sách và giải quyết nó theo cách này:

  1. Sắp xếp cả hai danh sách bởi id tăng dần sử dụng tùy chỉnh Comparator nếu các đối tượng trong danh sách không phải là Comparable
  2. lặp qua các yếu tố trong cả hai danh sách như trong pha hợp nhất trong merge sort algorithm, nhưng thay vì hợp nhất danh sách, bạn kiểm tra logic của mình.

Mã này sẽ được nhiều hơn hoặc ít hơn như thế này:

/* Main method */ 
private void execute(Collection<Foo> oldSet, Collection<Foo> newSet) { 
    List<Foo> oldList = asSortedList(oldSet); 
    List<Foo> newList = asSortedList(newSet); 

    int oldIndex = 0; 
    int newIndex = 0; 
    // Iterate over both collections but not always in the same pace 
    while(oldIndex < oldList.size() 
     && newIndex < newIndex.size()) { 
    Foo oldObject = oldList.get(oldIndex); 
    Foo newObject = newList.get(newIndex); 

    // Your logic here 
    if(oldObject.getId() < newObject.getId()) { 
     doRemove(oldObject); 
     oldIndex++; 
    } else if(oldObject.getId() > newObject.getId()) { 
     doAdd(newObject); 
     newIndex++; 
    } else if(oldObject.getId() == newObject.getId() 
      && isModified(oldObject, newObject)) { 
     doUpdate(oldObject, newObject); 
     oldIndex++; 
     newIndex++; 
    } else { 
     ... 
    } 
    }// while 

    // Check if there are any objects left in *oldList* or *newList* 

    for(; oldIndex < oldList.size(); oldIndex++) { 
    doRemove(oldList.get(oldIndex)); 
    }// for(oldIndex) 

    for(; newIndex < newList.size(); newIndex++) { 
    doAdd(newList.get(newIndex)); 
    }// for(newIndex) 
}// execute(oldSet, newSet) 

/** Create sorted list from collection 
    If you actually perform any actions on input collections than you should 
    always return new instance of list to keep algorithm simple. 
*/ 
private List<Foo> asSortedList(Collection<Foo> data) { 
    List<Foo> resultList; 
    if(data instanceof List) { 
    resultList = (List<Foo>)data; 
    } else { 
    resultList = new ArrayList<Foo>(data); 
    } 
    Collections.sort(resultList) 
    return resultList; 
} 
34

thư viện commons.collections Apache có một lớp CollectionUtils cung cấp phương pháp dễ dàng sử dụng cho Bộ sưu tập thao tác/kiểm tra, chẳng hạn như giao nhau, sự khác biệt và công đoàn.

org.apache.commons.collections.CollectionUtils tài liệu API là here.

+0

+1 cho thấy một URL thư viện –

+1

rắn là không có sẵn nữa. :( –

+0

http://commons.apache.org/proper/commons-collections/javadocs/api-4.0/index.html –

2

Tôi nghĩ cách dễ nhất để làm điều đó là sử dụng apache collection api - CollectionUtils.subtract (list1, list2) miễn là danh sách có cùng loại.

-2

Để chia sẻ danh sách hoặc bộ chúng tôi có thể sử dụng Arrays.equals(object[], object[]). Nó sẽ chỉ kiểm tra các giá trị. Để có được Object[], chúng tôi có thể sử dụng phương thức Collection.toArray().

20

Bạn có thể sử dụng Java 8 dòng, ví dụ

set1.stream().filter(s -> set2.contains(s)).collect(Collectors.toSet()); 

hoặc Sets lớp từ Guava:

Set<String> intersection = Sets.intersection(set1, set2); 
Set<String> difference = Sets.difference(set1, set2); 
Set<String> symmetricDifference = Sets.symmetricDifference(set1, set2); 
Set<String> union = Sets.union(set1, set2); 
+0

ổi là lựa chọn tốt nhất, nhờ –

+1

Trong khi ông gọi những bộ sưu tập này là "bộ", loại thực tế là ' Bộ sưu tập ', do đó, không giống như trong trường hợp thực tế' Bộ', các bản sao không nằm ngoài câu hỏi. –

0
public static boolean doCollectionsContainSameElements(
     Collection<Integer> c1, Collection<Integer> c2){ 

    if (c1 == null || c2 == null) { 
     return false; 
    } 
    else if (c1.size() != c2.size()) { 
     return false; 
    } else {  
     return c1.containsAll(c2) && c2.containsAll(c1); 
    }  
} 
Các vấn đề liên quan