Как эффективно сравнивать наборы?

Учитывая два набора: как их эффективно сравнить в Java?

  • (а) сохраните их как Lists, отсортируйте их и сравните. (Comparable)
  • (b) сохранить их как Sets и сравнить hashCode наборов?

фон:

необходимо провести много сравнений. Наборы небольшие (обычно ‹ 5 элементов в наборе).


person user1654885    schedule 13.11.2012    source источник
comment
set1.equals(set2); возвращает true, если 2 набора содержат одни и те же элементы...   -  person assylias    schedule 13.11.2012
comment
спасибо за этот ответ. но это более эффективно, чем хранить наборы в списках и сортировать их + сравнение?   -  person user1654885    schedule 13.11.2012
comment
Вы реализуете Set самостоятельно? Потому что у существующих уже есть equals? Или вы говорите о сравнении их для заказа (если да, то объясните, как это должно работать)?   -  person Thilo    schedule 13.11.2012
comment
нет. на самом деле я могу хранить свои данные в наборах или списках. я просто хочу сравнить наборы (например, два хеш-набора, наборы деревьев и т. д.), что является наиболее эффективным. и мне интересно, как сравнить их эффективным способом.   -  person user1654885    schedule 13.11.2012
comment
Два упомянутых вами способа, скорее всего, дадут разные результаты, и я думаю, что наборы, естественно, несопоставимы......   -  person luiges90    schedule 13.11.2012
comment
Что вы имеете в виду под сравнением? Посмотрите, равны ли они (т.е. содержат ли они равные элементы)? Или посмотреть, какой из них идет первым/больше?   -  person Thilo    schedule 13.11.2012
comment
нет не будут. и множества сопоставимы. (каждый элемент setA содержится в setB и наоборот)...   -  person user1654885    schedule 13.11.2012
comment
Они сопоставимы с точки зрения равенства, но если вы хотите упорядочить наборы, вам понадобится какой-то способ ранжировать один набор по сравнению с другим.   -  person The Cat    schedule 13.11.2012
comment
если вы имеете в виду, что каждый элемент setA содержится в setB, используйте docs.oracle.com/javase/1.4.2/docs/api/java/util/   -  person luiges90    schedule 13.11.2012
comment
ок извини моя вина. Я должен был определить сравнение раньше: я хотел бы проверить, состоят ли два набора из одних и тех же элементов.   -  person user1654885    schedule 13.11.2012


Ответы (4)


Правильный способ сравнения двух наборов — использовать метод equals. Я бы не беспокоился о производительности, если бы вы не доказали, что это часть вашего кода, вызывающая проблемы с производительностью (в чем я сомневаюсь). И, учитывая размер ваших наборов (5 элементов), это будет очень быстро (вероятно, меньше миллисекунды).

сохраняйте их в виде списков, сортируйте их и сравнивайте. (сопоставимо)

конечно будет медленнее, так как вам нужно будет копировать элементы, сортировать их и сравнивать.

сохранить их как наборы и сравнить хэш-код наборов?

если 2 набора равны (имеют одинаковое содержимое), они будут иметь одинаковый хэш-код. Обратное неверно: 2 набора с разным содержанием могут иметь одинаковый хэш-код. Также обратите внимание, что, например, для HashSet хэш-код вычисляется путем перебора всех элементов, поэтому это не бесплатная операция.

person assylias    schedule 13.11.2012
comment
Субмиллисекунда будет медленной. Есть хороший шанс, что это будет субмикросекунда, и даже меньше, если наборы большую часть времени не равны. - person Stephen C; 13.11.2012
comment
Для HashSets это сводится к пяти поискам хэша; для самого глупого подхода, который только можно себе представить, двух пятиэлементных списков, это все равно повлечет за собой в худшем случае 25 equals сравнений. - person Marko Topolnik; 13.11.2012

Что не так с равно? В документах указано, что он возвращает true, если оба имеют одинаковый размер, и если containsAll() возвращает true, для меня это звучит довольно эффективно.

В любом случае вы не должны никогда сравнивать хэш-код для проверки на равенство, два разных объекта могут иметь одинаковый хэш-код.

Обновление: Как отмечено в комментариях (и в ответе assylias), хэш-код можно использовать как часть логики проверки на равенство (разные хэш-коды подразумевают разные объекты, но не наоборот). Мое замечание выше означает, что одного хэш-кода (в общем случае) недостаточно.

person leonbloy    schedule 13.11.2012
comment
-1 за примечание хэш-кода. Если у вас есть готовый хэш-код, вы должны использовать его. Потому что, если коды различаются, объекты не равны. Вот как реализовано String#equals. (Конечно, если хэш-коды равны, это ничего вам не говорит). - person Thilo; 13.11.2012
comment
@Thilo На самом деле в случае набора хэш-код не кэшируется, как в String, поэтому для его вычисления требуется цикл для каждого элемента. - person assylias; 13.11.2012
comment
Да, хэш-код, вероятно, не кэшируется в Set (вы не можете точно сказать, поскольку Set — это интерфейс, в неизменяемом Set он вполне может быть). Но Set должен сначала посмотреть на size(), который работает почти так же. - person Thilo; 13.11.2012
comment
Несмотря на неправильную формулировку, хэш-код набора не должен абсолютно использоваться для определения его равенства другому набору. Редактирование в порядке, но не голосование против, imo. - person Perception; 13.11.2012

Если у вас есть два HashSet, сравнение их по Set.equals будет O(n), потому что нужно перебирать только один набор, а другой будет проверяться contains, который сам является O(1).

Обратите внимание, что для таких маленьких наборов, как ваш, разница между O(n) и O(n2) незначительна, поэтому даже самые наивные подходы дадут хорошую производительность.

person Marko Topolnik    schedule 13.11.2012

Предположим, вы хотите сравнить, есть ли в set1 точно такие же элементы set2.

set1.equals(set2), а также set2.equals(set1), чтобы убедиться, что они полностью одинаковы.

person user1447445    schedule 13.11.2012
comment
Необходимо только одно сравнение. Equals является симметричным: set1.equals(set2) всегда будет возвращать тот же результат, что и set2.equals(set1) (если, конечно, один из них не равен нулю). - person assylias; 13.11.2012