Щось на зразок "містить будь-яке" для набору Java?


307

У мене два набори, A і B, одного типу.

Я повинен знайти, чи містить A який-небудь елемент із множини B.

Що було б найкращим способом зробити це без повторення наборів? Бібліотека Набір має contains(object)і containsAll(collection), але не containsAny(collection).


4
Ви намагаєтесь уникнути повторення з міркувань ефективності чи чистоти коду?
yshavit

Відповіді:


527

Не Collections.disjoint(A, B)працювало б? З документації:

Повертається, trueякщо дві вказані колекції не мають спільних елементів.

Таким чином, метод повертається, falseякщо колекції містять якісь загальні елементи.


17
Віддайте перевагу іншим рішенням, оскільки воно не змінює жодного з наборів і не створює нового.
devconsole

7
І є стандартним JRE, і працює з будь-якими колекціями, а не просто встановленими.
П’єр Генрі

4
Я не думаю, що це найшвидше, воно не матиме короткого замикання, коли знайдеться перший елемент перетину.
Бен Хорнер

7
Насправді це коротке замикання, як тільки знайде перший загальний елемент
Xipo


156

Stream::anyMatch

Оскільки Java 8 ви могли використовувати Stream::anyMatch.

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

1
Це саме те, що я шукав! Дякую :-) Я також не знав, що ти можеш використовувати змінні з синтаксисом ::!
Дантістон

1
@blevert, ви могли б пояснити, що відбувається всередині будь-якогоMatch?
Кріштіано

7
@ Cristiano тут anyMatchпередасть усі елементи setAта зателефонує setB.contains()до них. Якщо "true" повертається для будь-якого з елементів, вираз у цілому буде оцінено як true. Сподіваюся, що це допомогло.
Алекс Вулай


31

Хороший спосіб реалізувати файл ContentAny для наборів - це використання Guava Sets.intersection () .

containsAnyповерне a boolean, тому дзвінок виглядає так:

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

Це повертає істинне, якщо множини непересічні, інакше помилкові. Часова складність цього, ймовірно, трохи краща, ніж retainAll, тому що вам не потрібно робити клонування, щоб уникнути зміни вашого оригінального набору.


3
Єдиним недоліком використання цього підходу є те, що ви повинні включити бібліотеки guava. Я думаю, що це не є недоліком, оскільки API колекцій Google дуже сильний.
Мохаммед Аднан

@DidierL більшість функцій утиліти Guava Collections, включаючи цю, повертають представлення структур даних. Тож у цій справі немає "побудови набору", щоб хвилюватися. Про реалізацію цікаво прочитати тут та / або побачити javadoc: google.github.io/guava/releases/21.0/api/docs/com/google/common/…
chut

@MohammadAdnan Ще одним недоліком є ​​те, що він обчислює повне перехрестя - якщо set1 та set2 дуже великі, це було б значно більш ресурсомістким (як для процесора, так і для пам'яті), ніж просто перевірка, чи є у них спільний предмет.
Марксама


16

Я використовую org.apache.commons.collections.CollectionUtils

CollectionUtils.containsAny(someCollection1, someCollection2)

Це все! Повертає істину якщо принаймні один елемент є в обох колекціях.

Проста у використанні, а назва функції є більш сугестивною.


5

Використовуйте retainAll()в інтерфейсі Set. Цей спосіб забезпечує перетин елементів, загальних для обох наборів. Додаткову інформацію див. У документах API.


Якщо сенс уникнути ітерації в ефективності, retainAllнапевно, не допоможе. Її реалізація в AbstractCollectionітераціях.
yshavit

1
yshavit правильний. Зважаючи на те, що ОП шукає, чи існує який-небудь елемент в обох наборах, належний алгоритм мав би час O(1)роботи в кращому випадку, тоді як retainAllмав би щось уздовж рядків O(N)(це залежало б від розміру лише 1 набору) найкращий час роботи.
Зейчин

3

Я рекомендую створити HashMapз набору A, а потім повторити через множину B і перевірити, чи є якийсь елемент B в A. Це запуститься в O(|A|+|B|)часі (оскільки не буде зіткнень), тоді як retainAll(Collection<?> c)повинно запускатися в O(|A|*|B|)часі.


3

Існує трохи грубий спосіб зробити це. Якщо і тільки якщо набір A містить якийсь елемент B, ніж виклик

A.removeAll(B)

змінить набір A. У цій ситуації RemoveAll поверне справжнє значення (Як зазначено в документах RemoveAll ). Але, ймовірно, ви не хочете змінювати набір A, щоб ви могли подумати діяти над копією, як-от так:

new HashSet(A).removeAll(B)

і повернене значення буде істинним, якщо множини не відрізняються, тобто вони мають не порожній перетин.

Також дивіться колекції Apache Commons


2

Ви можете скористатися методом retainAll і отримати перетин двох ваших множин.


У більшості випадків потрібно зберігати оригінальний набір, тому для retainAllйого використання необхідно зробити копію оригінального набору. Тоді ефективніше використовувати, HashSetяк запропонував Зейчин .
Петро Пудлак

Це зміна стану, а не перевірка стану
Бен
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.