Коли дві колекції Java рівні?

Метод equals в Java Collections Framework має дуже інтуїтивно зрозуміле значення: дві колекції рівні, якщо їх елементи рівні, з урахуванням порядку елементів, якщо це релевантно. Але чи означає це, що змінювана колекція може бути рівною незмінній? А що щодо впорядкованих і відсортованих колекцій?

pic

“Lego Classic Space Minifigs” by Dutch Treat / CC BY 2.0

Давайте почнемо з того, щоб додати деякі деталі до визначення методу equals в Java Collections Framework. Насправді є два визначення: одне для множин, і одне для списків. Дві множини рівні, якщо вони містять однакову кількість елементів і всі елементи однієї множини містяться в іншій (і навпаки). Те ж саме стосується списків, але для того, щоб списки були рівні, їх елементи також повинні бути в тому ж порядку.

У більшості випадків це визначення рівності є тим, що вам потрібно при порівнянні двох колекцій. За збігом обставин (або, можливо, не зовсім випадково, оскільки мова Java ретельно розроблена), ці визначення добре працюють, коли множини та списки використовуються як поля в Java записях. Однак є випадки, коли вам потрібно більш суворе або більш м’яке визначення рівності між колекціями. Прикладом може бути ситуація, коли ви пишете модульний тест і хочете порівняти дві колекції в секції assert. Одна з цих колекцій — це фактичний результат виклику методу, а інша — це подання того, що ви очікуєте.

Суворіші визначення рівності

В деяких випадках ви захочете перевірити не тільки те, чи колекція містить очікуваний вміст, але й чи є вона змінною чи ні. Як виявляється, перевірка змінності є дуже складною задачею в Java Collections Framework. Це точно не є властивістю, яку можна перевірити через API. Ви, звичайно, можете виконати перевірку на клас колекції під час виконання тесту та зробити висновки на основі цього, але я не рекомендую це робити. Альтернативно, ви можете спробувати змінити колекцію як частину вашого тесту, але навіть це не дасть вам гарантії, лише вказівку. Цілком можливо реалізувати колекції в Java Collections Framework, де змінність змінюється протягом життєвого циклу колекції. Те, що точно відомо, це те, що це не є частиною визначення методу equals, тому так, змінювана колекція може бути рівною незмінній.

Те ж саме стосується відсортованих множин, навіть якщо у них різні компаратори. Це означає, що якщо ви хочете написати модульний тест для методу, що повертає SortedSet, і хочете переконатися, що результат не тільки містить правильні елементи, але й правильний компаратор, вам потрібно додати явне твердження для цього у ваш тест. Насправді, якщо ви залежатимете лише від методу equals, SortedSet може бути рівним HashSet або будь-якому іншому класу, що реалізує інтерфейс Set.

Хоча ці два випадки здаються проблемними для модульних тестів, у більшості випадків це є перевагою того, що метод equals має таке визначення в Java Collections Framework.
Це дозволяє вам писати перевірки, як-от Assert.assertEquals(Set.of(“foo”, “bar”), actual), не турбуючись про те, який саме тип набору створює метод фабрики Set.of.

М’якіше визначення рівності

Але інколи ви стикаєтеся з протилежною проблемою. Іноді вам потрібно те, що інші фреймворки колекцій називають «мішком»: набір, який може обробляти дублікати елементів. У таких випадках можна використати список, але тільки якщо ви можете обійти обмеження на порядок його елементів.

Для модульних тестів сортування елементів списку перед порівнянням у перевірці може бути рішенням. Але це працюватиме лише в тому випадку, якщо ви зможете підготувати список для порівняння до виклику методу equals. Якщо ви намагаєтесь використовувати список як мішок для поля в Java записі, то виникнуть проблеми. Немає способу прибрати вимогу до порядку з методу equals для списку, впровадити сортування перед порівнянням або налаштувати Java запис для використання альтернативного методу equals для порівняння поля.

Впорядковані та відсортовані колекції

А як щодо впорядкованих та відсортованих колекцій: чи можуть вони бути рівними, якщо містять однакові елементи, а компаратор у відсортованій колекції сортує елементи в тому ж порядку, що й у впорядкованій колекції? Коротка відповідь — ні, не можуть. Множини можуть бути відсортовані, але за визначенням вони не можуть бути впорядкованими. Списки, з іншого боку, за визначенням впорядковані, а не відсортовані, навіть якщо наявність методу sort може ввести вас в оману. Метод sort перерозподіляє елементи за допомогою компаратора, але як тільки метод завершується, компаратор забувається списком. Список може виглядати відсортованим, але насправді він все ще впорядкований.

Це означає, що таке порівняння як Assert.assertEquals(List.of(“foo”, “bar”), aSortedSet) не спрацює. Так, може мати сенс використовувати впорядковану колекцію для перевірки, що відсортована множина містить правильні елементи, відсортовані в правильному порядку. Але використовуючи списки та множини з Java Collections Framework, результат порівняння завжди буде false. Швидше за все, вам слід мати один або кілька модульних тестів, що перевіряють, чи містить відсортована множина правильний вміст і компаратор, а також кілька інших тестів, що перевіряють сортування, виконане компаратором.

Для повноти: якщо ви дійсно хочете порівнювати впорядковані та відсортовані колекції, ви, звичайно, завжди можете реалізувати свій власний тип(и) впорядкованих та відсортованих колекцій, навіть як реалізацію інтерфейсу Collection в Java Collections Framework. Однак вам доведеться тримати обидва типи поза ієрархією List і Set. Якщо ні, ви порушите контракт для методу equals в будь-якому з класів List або Set, або в обох.

Коли дві колекції дійсно рівні?

І це зводить нас назад до питання в заголовку цієї статті, але на фундаментальному рівні: коли дві колекції Java рівні? Відповідь Java Collections Framework працює в більшості контекстів, але не у всіх. Тому єдина правильна відповідь — це: залежить, як зазвичай буває в програмній інженерії. Пам’ятайте, що колекції, множини та списки — це дуже загальні поняття, тому очікувати відповіді, яка буде працювати на сто відсотків у будь-якому контексті, було б несправедливо.

Перекладено з: When Are Two Java Collections Equal?

Leave a Reply

Your email address will not be published. Required fields are marked *