Một cái gì đó như 'chứa bất kỳ' cho bộ Java?


307

Tôi có hai bộ, A và B, cùng loại.

Tôi phải tìm xem A có chứa phần tử nào trong tập B.

Điều gì sẽ là cách tốt nhất để làm điều đó mà không lặp đi lặp lại qua các bộ? Thư viện Set có contains(object)containsAll(collection), nhưng không containsAny(collection).


4
Bạn đang cố gắng tránh lặp đi lặp lại vì lý do hiệu quả, hay vì sự sạch sẽ của mã?
yshavit

Câu trả lời:


527

Sẽ không Collections.disjoint(A, B)làm việc? Từ tài liệu:

Trả về truenếu hai bộ sưu tập được chỉ định không có phần tử chung.

Do đó, phương thức trả về falsenếu các tập hợp chứa bất kỳ phần tử phổ biến nào.


17
Thích điều này hơn các giải pháp khác vì nó không sửa đổi một trong hai bộ hoặc tạo một bộ mới.
devconsole

7
Và là JRE tiêu chuẩn, và hoạt động với mọi Bộ sưu tập, không chỉ được thiết lập.
Pierre Henry

4
Tôi không nghĩ đây là tốc độ nhanh nhất, nó sẽ không bị đoản mạch khi tìm thấy yếu tố đầu tiên của giao lộ.
Ben

7
Trên thực tế, nó sẽ bị đoản mạch ngay khi tìm thấy phần tử phổ biến đầu tiên
Xipo


156

Stream::anyMatch

Vì Java 8 bạn có thể sử dụng Stream::anyMatch.

setA.stream().anyMatch(setB::contains)

1
Điều này thật đúng với gì mà tôi đã tìm kiếm! Cảm ơn :-) Tôi cũng không biết bạn có thể sử dụng các biến với cú pháp ::!
dantiston

1
@blevert, bạn có thể giải thích những gì xảy ra bên trong anyMatch không?
Cristiano

7
@Cristiano ở đây, anyMatchsẽ truyền phát tất cả các yếu tố từ setAvà gọi setB.contains()tất cả chúng. Nếu "true" được trả về cho bất kỳ phần tử nào, toàn bộ biểu thức sẽ đánh giá là true. Hy vọng điều này sẽ giúp.
Alex Vulaj

2
@Cristiano docs.oracle.com/javase/8/docs/api/java/util/stream/ mẹo
Lluis Martinez

31

Một cách tốt để triển khai chứaAny cho các bộ là sử dụng Guava Sets.intersection () .

containsAnysẽ trả về a boolean, vì vậy cuộc gọi trông như sau:

Sets.intersection(set1, set2).isEmpty()

Điều này trả về đúng nếu các tập hợp rời rạc, nếu không thì sai. Độ phức tạp thời gian của điều này có thể tốt hơn một chút so với giữ lại Tất cả vì bạn không phải thực hiện bất kỳ việc nhân bản nào để tránh sửa đổi bộ gốc của mình.


3
Nhược điểm duy nhất của việc sử dụng phương pháp này là bạn phải bao gồm các thư viện ổi. Điều mà tôi nghĩ là không bất lợi vì API bộ sưu tập của Google rất mạnh.
Mohammad Adnan

@DidierL hầu hết các chức năng tiện ích của Bộ sưu tập Guava, bao gồm cả chức năng này, trả về các khung nhìn của cấu trúc dữ liệu. Vì vậy, không có "xây dựng tập hợp" để lo lắng trong trường hợp này. Việc triển khai rất thú vị khi đọc ở đây và / hoặc xem javadoc: google.github.io/guava/release/21.0/api/docs/com/google/common/ trộm
chut

@MohammadAdnan Một nhược điểm khác là nó tính toán giao điểm đầy đủ - nếu set1 và set2 rất lớn, điều này sẽ tốn nhiều tài nguyên hơn (cả CPU và bộ nhớ) so với việc chỉ kiểm tra xem chúng có điểm chung nào không.
Marxama


16

Tôi sử dụng org.apache.commons.collections.CollectionUtils

CollectionUtils.containsAny(someCollection1, someCollection2)

Đó là tất cả! Trả về true nếu có ít nhất một phần tử trong cả hai bộ sưu tập.

Sử dụng đơn giản, và tên của chức năng được gợi ý nhiều hơn.


5

Sử dụng retainAll()trong giao diện Đặt. Phương pháp này cung cấp một giao điểm của các phần tử phổ biến trong cả hai bộ. Xem tài liệu API để biết thêm thông tin.


Nếu quan điểm tránh lặp lại là hiệu quả, retainAllcó lẽ sẽ không có ích. Nó thực hiện trong AbstractCollectionlặp đi lặp lại.
yshavit

1
yshavit là chính xác. Cho rằng OP đang muốn xem liệu phần tử nào tồn tại trong cả hai tập hợp không, một thuật toán thích hợp sẽ có O(1)thời gian chạy trong trường hợp tốt nhất, trong khi đó retainAllsẽ có thứ gì đó dọc theo dòng O(N)(nó sẽ phụ thuộc vào kích thước chỉ 1 bộ) thời gian chạy tốt nhất.
Zéychin

3

Tôi khuyên bạn nên tạo a HashMaptừ tập A, sau đó lặp qua tập B và kiểm tra xem có phần tử nào của B ở A. Điều này sẽ chạy O(|A|+|B|)đúng lúc (vì sẽ không có va chạm), trong khi retainAll(Collection<?> c)phải chạy O(|A|*|B|)kịp thời.


3

Có một phương pháp hơi thô để làm điều đó. Nếu và chỉ khi tập A chứa một số phần tử B hơn cuộc gọi

A.removeAll(B)

sẽ sửa đổi tập A. Trong tình huống này, remove ALL sẽ trả về true (Như đã nêu tại remove ALL docs ). Nhưng có lẽ bạn không muốn sửa đổi bộ A để bạn có thể nghĩ sẽ hành động trên một bản sao, như cách này:

new HashSet(A).removeAll(B)

và giá trị trả về sẽ đúng nếu các tập hợp không khác biệt, nghĩa là chúng có giao điểm không trống.

Cũng xem Bộ sưu tập Apache Commons


2

Bạn có thể sử dụng phương thức retAll và nhận giao điểm của hai bộ của bạn.


Trong hầu hết các trường hợp, người ta cần giữ bộ gốc, vì vậy để sử dụng retainAll, cần phải tạo một bản sao của bộ gốc. Sau đó, nó hiệu quả hơn để sử dụng HashSettheo đề xuất của Zéychin .
Petr Pudlák

Đó là thay đổi trạng thái, không phải kiểm tra điều kiện
Ben
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.