Як підтримувати унікальний список на Java?


104

Як створити список унікальних / відмінних об’єктів (без дублікатів) на Java?

Зараз я це HashMap<String, Integer>роблю, оскільки ключ перезаписаний, і, отже, в кінці ми можемо отримати HashMap.getKeySet()який був би унікальний. Але я впевнений, що для цього має бути кращий спосіб, оскільки ціннісна частина тут втрачається.

Відповіді:


164

Можна використовувати набір реалізацію :

Деякі відомості від JAVADoc:

Колекція, яка не містить дублікатів елементів . Більш формально, набори не містять пар елементів e1 і e2, таких, що e1.equals (e2), і не більше одного нульового елемента. Як випливає з назви, цей інтерфейс моделює математичну абстракцію.

Примітка. Велику обережність слід дотримуватися, якщо в якості елементів набору використовуються змінні предмети. Поведінка набору не визначається, якщо значення об'єкта змінюється таким чином, що впливає на порівняння, тоді як об'єкт є елементом у наборі. Особливим випадком цієї заборони є те, що набір не може містити себе як елемент. "

Це такі реалізації:

  • HashSet

    Цей клас пропонує постійну продуктивність у часі для основних операцій (додавання, видалення, вміст та розмір), припускаючи, що хеш-функція належним чином розподіляє елементи між відрами. Ітерація над цим набором вимагає часу, пропорційного сумі розміру екземпляра HashSet (кількості елементів) плюс "ємності" резервного екземпляра HashMap (кількості відра). Таким чином, дуже важливо не встановлювати занадто високу початкову потужність (або коефіцієнт навантаження занадто низький), якщо важлива продуктивність ітерації.

    При ітерації HashSetпорядок виведених елементів не визначений.

  • LinkedHashSet

    Таблиця хешування та реалізація пов'язаного списку інтерфейсу Set, із передбачуваним порядком ітерації. Ця реалізація відрізняється від HashSet тим, що вона підтримує подвійно пов'язаний список, що проходить через усі його записи. Цей пов'язаний список визначає порядок ітерації, який є порядком, в якому елементи були вставлені в набір (порядок вставки). Зауважте, що порядок вставки не впливає, якщо елемент повторно вставлений у набір. (Елемент e знову вставляється у набір s, якщо s.add (e) викликається, коли s.con Sad (e) повернеться true перед викликом.)

    Отже, вихід коду вище ...

     Set<Integer> linkedHashSet = new LinkedHashSet<>();
     linkedHashSet.add(3);
     linkedHashSet.add(1);
     linkedHashSet.add(2);
    
     for (int i : linkedHashSet) {
         System.out.println(i);
     }

    ... обов'язково буде

    3
    1
    2
  • TreeSet

    Ця реалізація забезпечує гарантовану вартість журналу (n) часу для основних операцій (додавання, видалення та вміст). За замовчуванням він повертається на ітерацію елементів, відсортованих за їх " природним упорядкуванням ", тому код вище ...

     Set<Integer> treeSet = new TreeSet<>();
     treeSet.add(3);
     treeSet.add(1);
     treeSet.add(2);
    
     for (int i : treeSet) {
         System.out.println(i);
     }

    ... виведе це:

    1
    2
    3

    (Ви можете також передати Comparatorекземпляр TreeSetконструктору, змусивши його сортувати елементи в іншому порядку.)

    Зауважте, що впорядкування, що підтримується набором (незалежно від того, надається явний компаратор чи ні), повинно відповідати рівним, якщо воно належним чином реалізує інтерфейс Set. (Див. Порівняння або Порівняння для точного визначення відповідності рівним.) Це відбувається тому, що інтерфейс Set визначається з точки зору операції рівних, але екземпляр TreeSet виконує всі порівняння елементів, використовуючи метод його порівняння (або порівняння), тому два елементи, які вважаються рівними цим методом, з точки зору множини рівні. Поведінка набору чітко визначена, навіть якщо її упорядкування не відповідає рівним; він просто не виконує загальний контракт інтерфейсу Set.


Тепер я розгублений, який з них використовувати? Мені просто потрібно підтримувати список унікальних рядків. Таким чином, навіть коли додається існуючий рядок, він насправді повинен бути доданий.

1
Вибір за вами ... HashSet універсальний і швидкий, набір дерев замовлений, LinkedHashset зберігає порядок вставки ...
Frank

6
Це не СПИСОК ... тож не всі методи інтерфейсу СПИСОК доступні.
марколопи

2
Набір не є списком, я не можу шукати елементи за індексом у наборі за О (1) час (випадковий доступ).
wilmol

13

Я хочу прояснити тут деякі речі для оригінального плаката, на який інші натякали, але насправді прямо не заявили. Коли ви говорите, що хочете унікального списку, це саме визначення впорядкованого набору. Деякі інші ключові відмінності між інтерфейсом Set і інтерфейсом List полягають у тому, що Список дозволяє вказати індекс вставки. Отже, питання полягає в тому, чи справді вам потрібен інтерфейс списку (тобто для сумісності з сторонньою бібліотекою тощо), чи ви можете переробити програмне забезпечення для використання інтерфейсу Set? Ви також повинні врахувати, що ви робите з інтерфейсом. Чи важливо знайти елементи за їх індексом? Скільки елементів ви очікуєте у своєму наборі? Якщо у вас буде багато елементів, чи важливе замовлення?

Якщо вам справді потрібен Список, який просто має унікальне обмеження, є клас org.apache.commons.collections.list.SetUniqueList Apache Common Utils, який надасть вам інтерфейс списку та унікальне обмеження. Майте на увазі, це порушує інтерфейс List. Однак ви отримаєте кращу ефективність від цього, якщо вам буде потрібно шукати список за індексом. Якщо ви можете мати справу з інтерфейсом Set і у вас є менший набір даних, то LinkedHashSet може бути хорошим способом. Це просто залежить від дизайну та намірів вашого програмного забезпечення.

Знову ж таки, у кожної колекції є певні переваги та недоліки. Деякі швидкі вставки, але повільно зчитуються, деякі мають швидке зчитування, але повільні вставки та ін. Має сенс витратити досить багато часу на документацію про колекцію, щоб повністю дізнатися про більш точні деталі кожного класу та інтерфейсу.


3
Це не дає відповіді на запитання. Щоб критикувати або вимагати роз'яснення у автора, залиште коментар під їх публікацією - ви завжди можете коментувати свої власні публікації, і коли ви матимете достатню репутацію, ви зможете коментувати будь-яку публікацію .
Зак Сосьє

1
Насправді це дає відповідь. Якщо він просто хоче список, який діє як набір, використовуйте org.apache.commons.collections.list.SetUniqueList, але як програміст, він / ми повинні бути обережнішими за це і повинні думати більше про проблему. Якщо це робить мою відповідь кращою, "Як створити унікальний список на Java?" Список унікального списку = новий SetUniqueList (); ось так ....
Пол Конноллі

3
І Зак, я не намагаюся бути дурнем, але ти навіть прочитав мою відповідь перед коментарем? Або ти просто цього не розумієш? Якщо ви цього не розумієте, все в порядку - повідомте мене, і я розгорну тему. Я не думаю, що мені доведеться писати трактат про структури даних, щоб дати дружну відповідь на чиєсь запитання. Мені також не хочеться розглянути якийсь покірний спосіб формування моєї репутації в коментарях, коли я знаю відповідь, і ніхто інший її не надав.
Пол Конноллі

1
І, до речі, я не критикував і не вимагав від автора роз'яснень, я просто говорив, що він може A) швидко використовувати клас, який я йому дав, або B) витратити час, щоб дійсно зрозуміти відмінності між цими класами і відновити їх під його потреби. B, очевидно, займає більше часу, але призведе до кращого коду в довгостроковій перспективі.
Пол Конноллі

8

Скористайтеся new HashSet<String> прикладом:

import java.util.HashSet;
import java.util.Set;

public class MainClass {
  public static void main(String args[]) {
    String[] name1 = { "Amy", "Jose", "Jeremy", "Alice", "Patrick" };

    String[] name2 = { "Alan", "Amy", "Jeremy", "Helen", "Alexi" };

    String[] name3 = { "Adel", "Aaron", "Amy", "James", "Alice" };

    Set<String> letter = new HashSet<String>();

    for (int i = 0; i < name1.length; i++)
      letter.add(name1[i]);

    for (int j = 0; j < name2.length; j++)
      letter.add(name2[j]);

    for (int k = 0; k < name3.length; k++)
      letter.add(name3[k]);

    System.out.println(letter.size() + " letters must be sent to: " + letter);

  }
}

2
Тільки додавши викладену вище програму -> 11 листів потрібно надіслати на адресу: [Аарон, Аліса, Джеймс, Адель, Хосе, Джеремі, Емі, Алан, Патрік, Хелен, Алексі]
Аммад

4

Ви можете просто використовувати a HashSet<String>для підтримки колекції унікальних об'єктів. Якщо Integerзначення на вашій карті важливі, ви можете замість цього використати containsKeyметод карт, щоб перевірити, чи є ваш ключ вже на карті.


3

HashSet<String>(або) будь-яка Setреалізація може зробити цю роботу за вас.Setне дозволяти дублікатів.

Ось javadoc для HashSet.


2

Я не знаю, наскільки це ефективно, однак працював для мене в простому контексті.

List<int> uniqueNumbers = new ArrayList<>();

   public void AddNumberToList(int num)
    {
        if(!uniqueNumbers .contains(num)) {
            uniqueNumbers .add(num);
        }
    }

1

Можливо, ви хочете використовувати один із реалізуючих класів java.util.Set<E>інтерфейсу, наприклад java.util.HashSet<String> клас колекції.

Колекція, яка не містить дублікатів елементів. Більш формально, набори не містять пар елементів e1 і e2, таких, що e1.equals (e2), і не більше одного нульового елемента. Як випливає з назви, цей інтерфейс моделює математичну абстракцію.

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