Lấy sự khác biệt giữa hai bộ


160

Vì vậy, nếu tôi có hai bộ:

Set<Integer> test1 = new HashSet<Integer>();
test1.add(1);
test1.add(2);
test1.add(3);

Set<Integer> test2 = new HashSet<Integer>();
test2.add(1);
test2.add(2);
test2.add(3);
test2.add(4);
test2.add(5);

Có cách nào để so sánh chúng và chỉ có một bộ 4 và 5 được trả về không?


Bản sao có thể có của stackoverflow.com/questions/8064570/
Kẻ

11
Đây không phải là một bản sao chính xác: sự khác biệt và khác biệt đối xứng không giống nhau.
Simon Nickerson

Nếu test16, câu trả lời sẽ là 4,5,6? tức là bạn có muốn sự khác biệt đối xứng en.wikipedia.org/wiki/Symmetric_difference
Colin D

1
nếu test1 chứa 6, tôi muốn câu trả lời vẫn là 4, 5.
David Tunnell

Câu trả lời:


197

Thử cái này

test2.removeAll(test1);

Đặt # remove ALL

Xóa khỏi bộ này tất cả các thành phần của nó được chứa trong bộ sưu tập đã chỉ định (thao tác tùy chọn). Nếu bộ sưu tập được chỉ định cũng là một tập hợp, thao tác này sẽ sửa đổi hiệu quả bộ này sao cho giá trị của nó là chênh lệch tập không đối xứng của hai bộ.


43
Điều này sẽ hoạt động nhưng tôi nghĩ nó sẽ là một tính năng tốt để có các hoạt động được thiết lập như union, sự khác biệt được xây dựng trong java. Giải pháp trên sẽ sửa đổi tập hợp, trong nhiều tình huống chúng tôi không thực sự muốn điều đó.
Praveen Kumar

129
Làm thế nào Java có thể có mật để gọi cấu trúc dữ liệu này Setkhi nó không định nghĩa union, intersectionhoặc difference!!!
James Newman

10
Giải pháp này không hoàn toàn chính xác. Bởi vì thứ tự của test1 và test2 tạo ra sự khác biệt.
Bojan Petkovic

1
Sẽ test1.removeAll(test2);trả lại kết quả tương tự như test2.removeAll(test1);?
datv

3
@datv Kết quả sẽ khác. test1.removeAll(test2)là một bộ trống. test2.removeAll(test1){4, 5}.
im lặng

122

Nếu bạn sử dụng thư viện Guava (Bộ sưu tập Google cũ), có một giải pháp:

SetView<Number> difference = com.google.common.collect.Sets.difference(test2, test1);

Trả về SetViewlà một Set, nó là một đại diện trực tiếp mà bạn có thể thực hiện bất biến hoặc sao chép sang bộ khác. test1test2được giữ nguyên.


6
Lưu ý rằng thứ tự của test2 và test1 có vấn đề. Ngoài ra còn có đối xứngDifference () trong đó thứ tự không quan trọng.
datv

1
symmetricDifference()sẽ mang lại tất cả trừ giao lộ, đó không phải là những gì câu hỏi ban đầu yêu cầu.
Allenaz

16

Đúng:

test2.removeAll(test1)

Mặc dù điều này sẽ đột biến test2, vì vậy hãy tạo một bản sao nếu bạn cần bảo quản nó.

Ngoài ra, bạn có thể có nghĩa là <Integer>thay vì <int>.


7

Java 8

Chúng ta có thể sử dụng remove If trong đó có một biến vị ngữ để viết một phương thức tiện ích như:

// computes the difference without modifying the sets
public static <T> Set<T> differenceJava8(final Set<T> setOne, final Set<T> setTwo) {
     Set<T> result = new HashSet<T>(setOne);
     result.removeIf(setTwo::contains);
     return result;
}

Và trong trường hợp chúng tôi vẫn ở một số phiên bản trước thì chúng tôi có thể sử dụng remove ALL như:

public static <T> Set<T> difference(final Set<T> setOne, final Set<T> setTwo) {
     Set<T> result = new HashSet<T>(setOne);
     result.removeAll(setTwo);
     return result;
}

3

Nếu bạn đang sử dụng Java 8, bạn có thể thử một cái gì đó như thế này:

public Set<Number> difference(final Set<Number> set1, final Set<Number> set2){
    final Set<Number> larger = set1.size() > set2.size() ? set1 : set2;
    final Set<Number> smaller = larger.equals(set1) ? set2 : set1;
    return larger.stream().filter(n -> !smaller.contains(n)).collect(Collectors.toSet());
}

4
@Downvoter: Có lẽ bạn đã không nhận ra rằng các câu trả lời khác không kiểm tra xem cái nào Setlớn hơn ... Do đó, nếu bạn đang cố gắng trừ một aa nhỏ hơn Settừ lớn hơn Set, bạn sẽ nhận được kết quả khác nhau.
Josh M

40
bạn đang giả định rằng người tiêu dùng của hàm đó luôn muốn trừ tập nhỏ hơn. Đặt sự khác biệt là chống đối kháng ( en.wikipedia.org/wiki/Anticommutativity ). AB! = BA
Simon

7
Bất kể biến thể khác biệt nào bạn thực hiện, tôi sẽ sử dụng public static <T> Set<T> difference(final Set<T> set1, final Set<T> set2) {làm chữ ký, phương thức sau đó có thể sử dụng làm hàm tiện ích chung.
kap

1
@kap nhưng sau đó thêm một Comparator<T>để có thể tùy chỉnh so sánh vì equalskhông phải lúc nào cũng đủ.
gervais.b

6
Điều này sẽ dẫn đến kết quả không mong muốn vì thứ tự của hoạt động khác biệt có thể được chuyển đổi mà người dùng không biết. Phép trừ của một tập lớn hơn từ một tập nhỏ hơn được xác định rõ về mặt toán học và có rất nhiều trường hợp sử dụng cho nó.
Joel Cornett

3

Bạn có thể sử dụng CollectionUtils.disjunctionđể có được tất cả sự khác biệt hoặcCollectionUtils.subtract để có được sự khác biệt trong bộ sưu tập đầu tiên.

Đây là một ví dụ về cách làm điều đó:

    var collection1 = List.of(1, 2, 3, 4, 5);
    var collection2 = List.of(2, 3, 5, 6);
    System.out.println(StringUtils.join(collection1, " , "));
    System.out.println(StringUtils.join(collection2, " , "));
    System.out.println(StringUtils.join(CollectionUtils.subtract(collection1, collection2), " , "));
    System.out.println(StringUtils.join(CollectionUtils.retainAll(collection1, collection2), " , "));
    System.out.println(StringUtils.join(CollectionUtils.collate(collection1, collection2), " , "));
    System.out.println(StringUtils.join(CollectionUtils.disjunction(collection1, collection2), " , "));
    System.out.println(StringUtils.join(CollectionUtils.intersection(collection1, collection2), " , "));
    System.out.println(StringUtils.join(CollectionUtils.union(collection1, collection2), " , "));

3
Từ dự án nào CollectionUtilsđến từ đâu? Có phải 1 phải cho rằng đó là từ Bộ sưu tập Apache Commons?
Buhake Sindi

0

Chỉ cần đặt một ví dụ ở đây (hệ thống đang ở trong existingStatevà chúng tôi muốn tìm các phần tử để loại bỏ (các phần tử không có trong newStatenhưng hiện diện existingState) và các phần tử để thêm (các phần tử có trong newStatenhưng không có trong existingState):

public class AddAndRemove {

  static Set<Integer> existingState = Set.of(1,2,3,4,5);
  static Set<Integer> newState = Set.of(0,5,2,11,3,99);

  public static void main(String[] args) {

    Set<Integer> add = new HashSet<>(newState);
    add.removeAll(existingState);

    System.out.println("Elements to add : " + add);

    Set<Integer> remove = new HashSet<>(existingState);
    remove.removeAll(newState);

    System.out.println("Elements to remove : " + remove);

  }
}

sẽ tạo ra kết quả này:

Elements to add : [0, 99, 11]
Elements to remove : [1, 4]
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.