Якщо ви просто хочете знати, чи множини рівні, equals
метод on AbstractSet
реалізується приблизно, як показано нижче:
public boolean equals(Object o) {
if (o == this)
return true;
if (!(o instanceof Set))
return false;
Collection c = (Collection) o;
if (c.size() != size())
return false;
return containsAll(c);
}
Зверніть увагу, як вона оптимізує поширені випадки, коли:
- два об'єкти однакові
- інший об'єкт зовсім не є набором, і
- розміри двох комплектів різні.
Після цього containsAll(...)
повернеться, false
як тільки знайде елемент в іншому наборі, якого також немає в цьому наборі. Але якщо всі елементи присутні в обох наборах, потрібно буде перевірити їх усі.
Отже, найгірша ефективність має місце, коли два набори рівні, але не однакові об'єкти. Ці витрати , як правило , O(N)
або в O(NlogN)
залежності від реалізації this.containsAll(c)
.
І ви отримуєте близькі до найгірших показників, якщо набори великі і відрізняються лише невеликим відсотком елементів.
ОНОВЛЕННЯ
Якщо ви готові інвестувати час у реалізацію спеціального набору, існує підхід, який може покращити «майже той самий» випадок.
Ідея полягає в тому, що вам потрібно попередньо обчислити і кешувати хеш для всього набору, щоб ви могли отримати поточне значення хеш-коду набору в O(1)
. Потім ви можете порівняти хеш-код для двох наборів як прискорення.
Як ти міг реалізувати такий хеш-код? Добре, якщо встановлений хеш-код був:
- нуль для порожнього набору, і
- XOR усіх хеш-кодів елементів для порожнього набору,
тоді ви зможете дешево оновити кешований хеш-код набору щоразу, коли ви додавали або видаляли елемент. В обох випадках ви просто XOR хеш-код елемента з поточним встановленим хеш-кодом.
Звичайно, це передбачає, що хеш-коди елементів є стабільними, тоді як елементи є членами множин. Він також передбачає, що функція хеш-коду класів елементів дає гарне поширення. Це тому, що коли два встановлені хеш-коди однакові, вам все одно доведеться повернутися до O(N)
порівняння всіх елементів.
Ви можете взяти цю ідею трохи далі ... принаймні теоретично.
ПОПЕРЕДЖЕННЯ - Це дуже спекулятивно. "Думковий експеримент", якщо вам подобається.
Припустимо, у вашому класі встановлених елементів є метод повернення криптовалют контрольних сум для елемента. Тепер реалізуйте контрольні суми набору шляхом XORing контрольних сум, повернутих для елементів.
Що це купує у нас?
Ну, якщо припустити, що нічого не відбувається, вірогідність того, що будь-які два нерівні множинні елементи мають однакові N-бітові контрольні суми, є 2 -N . І ймовірність 2 неоднакових множин мають однакові N-бітові контрольні суми також 2 -N . Тож моя ідея полягає в тому, що ви можете реалізувати equals
як:
public boolean equals(Object o) {
if (o == this)
return true;
if (!(o instanceof Set))
return false;
Collection c = (Collection) o;
if (c.size() != size())
return false;
return checksums.equals(c.checksums);
}
Згідно з припущеннями, наведеними вище, це дасть вам неправильну відповідь лише один раз у 2- N час. Якщо ви зробите N досить великим (наприклад, 512 біт), ймовірність неправильної відповіді стає незначною (наприклад, приблизно 10 -150 ).
Мінус полягає в тому, що обчислення криптовалют для елементів є дуже дорогим, тим більше, що збільшується кількість біт. Тож вам справді потрібен дієвий механізм запам'ятовування контрольних сум. І це може бути проблематично.
І інший недолік полягає в тому, що ненульова ймовірність помилки може бути неприйнятною, незалежно від того, наскільки ймовірною є мала. (Але якщо це так ... як ви ставитеся до випадку, коли космічний промінь перевертає критичний біт? Або якщо він одночасно перевертає той самий біт у двох випадках надмірної системи?)