Как поддерживать уникальный список в Java?

104

Как создать список уникальных / отличных объектов (без дубликатов) в Java?

Прямо сейчас я использую HashMap<String, Integer>для этого, поскольку ключ перезаписан, и, следовательно, в конце мы можем получить то, HashMap.getKeySet()что будет уникальным. Но я уверен, что должен быть лучший способ сделать это, поскольку часть ценности здесь теряется.

Василий Бурк
источник

Ответы:

165

Вы можете использовать реализацию Set :

Некоторая информация из JAVADoc:

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

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

Это реализации:

  • HashSet

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

    При итерации HashSetпорядок полученных элементов не определен.

  • LinkedHashSet

    Реализация хэш-таблицы и связанного списка интерфейса Set с предсказуемым порядком итераций. Эта реализация отличается от HashSet тем, что поддерживает двусвязный список, проходящий через все его записи. Этот связанный список определяет порядок итераций, то есть порядок, в котором элементы были вставлены в набор (порядок вставки). Обратите внимание, что порядок вставки не изменяется, если элемент повторно вставляется в набор. (Элемент e повторно вставляется в набор s, если s.add (e) вызывается, когда s.contains (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

    Эта реализация обеспечивает гарантированные затраты времени log (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конструктору, чтобы он отсортировал элементы в другом порядке.)

    Обратите внимание, что порядок, поддерживаемый набором (независимо от того, предоставляется ли явный компаратор или нет), должен согласовываться с equals, если он должен правильно реализовать интерфейс Set. (См. Comparable или Comparator для получения точного определения согласованности с equals.) Это так, потому что интерфейс Set определен в терминах операции equals, но экземпляр TreeSet выполняет все сравнения элементов, используя свой метод compareTo (или compare), поэтому два элементы, которые считаются равными с помощью этого метода, равны с точки зрения множества. Поведение набора четко определено, даже если его порядок несовместим с equals; он просто не подчиняется общему соглашению интерфейса Set.

Фрэнк
источник
Теперь я не понимаю, что мне использовать? Мне просто нужно вести список уникальных строк. Таким образом, в основном, даже когда добавляется существующая строка, она должна быть добавлена.
1
Выбор за вами ... HashSet универсален и быстр, древовидный набор упорядочен, LinkedHashset сохраняет порядок вставки ...
Фрэнк
6
Это не СПИСОК ... поэтому не все методы интерфейса СПИСОК доступны.
marcolopes
2
Набор не является списком, я не могу искать элементы по индексу в наборе за время O (1) (произвольный доступ).
Wilmol
13

Я хочу прояснить некоторые вещи здесь для оригинального плаката, на которые другие ссылались, но на самом деле явно не заявили. Когда вы говорите, что хотите уникальный список, это и есть определение упорядоченного набора. Некоторые другие ключевые различия между интерфейсом Set и интерфейсом List состоят в том, что List позволяет вам указать индекс вставки. Итак, вопрос в том, действительно ли вам нужен интерфейс List (например, для совместимости со сторонней библиотекой и т. Д.), Или вы можете переделать свое программное обеспечение для использования интерфейса Set? Вы также должны учитывать, что вы делаете с интерфейсом. Важно ли находить элементы по их индексу? Сколько элементов вы ожидаете в своем наборе? Если у вас будет много элементов, важен ли их порядок?

Если вам действительно нужен список, который имеет только уникальное ограничение, существует класс Apache Common Utils org.apache.commons.collections.list.SetUniqueList, который предоставит вам интерфейс List и уникальное ограничение. Имейте в виду, что это нарушает интерфейс List. Однако вы получите лучшую производительность, если вам нужно будет искать в списке по индексу. Если вы можете иметь дело с интерфейсом Set и у вас небольшой набор данных, то LinkedHashSet может быть хорошим вариантом. Это просто зависит от дизайна и предназначения вашего программного обеспечения.

Опять же, у каждой коллекции есть свои преимущества и недостатки. Некоторые быстрые вставки, но медленные чтения, некоторые имеют быстрое чтение, но медленные вставки и т. Д. Имеет смысл потратить изрядное количество времени на документацию по коллекциям, чтобы полностью изучить более тонкие детали каждого класса и интерфейса.

Пол Коннолли
источник
3
Это не дает ответа на вопрос. Чтобы критиковать или запрашивать разъяснения у автора, оставьте комментарий под его сообщением - вы всегда можете комментировать свои собственные сообщения, и как только у вас будет достаточная репутация, вы сможете комментировать любое сообщение .
Zach Saucier
1
На самом деле он дает ответ. Если ему просто нужен список, который действует как Set, используйте org.apache.commons.collections.list.SetUniqueList, но как программист он / мы должны быть более осторожными и должны больше думать о проблеме. Если это делает мой ответ лучше: «Как создать уникальный список в Java?» List uniqueList = new SetUniqueList (); вот как ....
Пол Коннолли
3
И Зак, я не пытаюсь быть придурком, но ты вообще читал мой ответ до своего комментария? Или вы просто этого не понимаете? Если вы этого не понимаете, ничего страшного - дайте мне знать, и я расширю тему. Не думаю, что мне нужно писать трактат о структурах данных, чтобы дать дружеский ответ на чей-то вопрос. Я также не собираюсь прибегать к каким-то безропотным способам повышения репутации в отношении комментариев, когда я знаю ответ, а на самом деле его никто не предоставил.
Пол Коннолли
1
И, кстати, я не критиковал и не просил разъяснений у автора, я просто говорил, что он может либо А) быстро использовать класс, который я ему дал, или Б) найти время, чтобы действительно понять различия между этими классами и связать их к его потребностям. B, очевидно, занимает больше времени, но в долгосрочной перспективе приведет к улучшению кода.
Пол Коннолли
9

Используйте 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);

  }
}
tim_a
источник
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), и не более одного нулевого элемента. Как следует из его названия, этот интерфейс моделирует абстракцию математического множества.

Йогендра Сингх
источник