Как удалить дубликаты из списка? - PullRequest
55 голосов
/ 17 мая 2010

Я хочу удалить дубликаты из списка, но то, что я делаю, не работает:

List<Customer> listCustomer = new ArrayList<Customer>();    
for (Customer customer: tmpListCustomer)
{
  if (!listCustomer.contains(customer)) 
  {
    listCustomer.add(customer);
  }
 }

Ответы [ 15 ]

88 голосов
/ 24 февраля 2011

При условии, что вы хотите сохранить текущий заказ и не хотите Set, возможно, самое простое:

List<Customer> depdupeCustomers =
    new ArrayList<>(new LinkedHashSet<>(customers));

Если вы хотите изменить исходный список:

Set<Customer> depdupeCustomers = new LinkedHashSet<>(customers);
customers.clear();
customers.addAll(dedupeCustomers);
48 голосов
/ 17 мая 2010

Если этот код не работает, вы, вероятно, не реализовали equals(Object) в классе Customer надлежащим образом.

Предположительно, есть какой-то ключ (назовем его customerId), который однозначно идентифицирует клиента; например,

class Customer {
    private String customerId;
    ...

Подходящее определение equals(Object) будет выглядеть так:

    public boolean equals(Object obj) {
        if (obj == this) {
            return true;
        }
        if (!(obj instanceof Customer)) {
            return false;
        }
        Customer other = (Customer) obj;
        return this.customerId.equals(other.customerId);
    }

Для полноты вы должны также реализовать hashCode, чтобы два равных объекта Customer возвращали одинаковое значение хеш-функции. Соответствующее hashCode для приведенного выше определения equals будет:

    public int hashCode() {
        return customerId.hashCode();
    }

Стоит также отметить, что это не эффективный способ удаления дубликатов, если список большой. (Для списка с N клиентами вам нужно будет выполнить N*(N-1)/2 сравнений в худшем случае, т. Е. Когда нет дубликатов.) Для более эффективного решения вы должны использовать что-то вроде HashSet для проверки дубликатов.

25 голосов
/ 12 июля 2016

Java 8 обновление
Вы можете использовать поток массива, как показано ниже:

Arrays.stream(yourArray).distinct()
                    .collect(Collectors.toList());
13 голосов
/ 17 мая 2010

Заказчик выполняет договор equals()?

Если он не реализует equals() и hashCode(), тогда listCustomer.contains(customer) проверит, существует ли точно такой же экземпляр в списке (под экземпляром я имею в виду тот же объект адрес памяти и т. д.). Если вы ищете, чтобы проверить, есть ли в списке тот же самый Клиент (возможно, тот же самый клиент, если у них одно и то же имя клиента или номер клиента), тогда вам нужно будет переопределите equals(), чтобы убедиться, что он проверяет, соответствуют ли соответствующие поля (например, имена клиентов).

Примечание: не забудьте переопределить hashCode(), если вы собираетесь переопределить equals()! В противном случае у вас могут возникнуть проблемы с вашими HashMaps и другими структурами данных. Чтобы лучше понять, почему это так и каких ошибок следует избегать, подумайте о том, чтобы взглянуть на главы Effective Java Джоша Блоха, посвященные equals() и hashCode() (ссылка содержит только информацию о том, почему вы должны реализовать hashCode() когда вы реализуете equals(), но есть хороший обзор того, как переопределить equals() тоже).

Кстати, есть ли ограничения на порядок на вашем устройстве? Если нет, то немного более простой способ решить эту проблему - использовать Set<Customer> примерно так:

Set<Customer> noDups = new HashSet<Customer>();
noDups.addAll(tmpListCustomer);
return new ArrayList<Customer>(noDups);

Что удалит дубликаты для вас, поскольку наборы не допускают дубликатов. Однако это приведет к потере любого порядка, примененного к tmpListCustomer, так как HashSet не имеет явного порядка (вы можете обойти это, используя TreeSet, но это не совсем относится к вашему вопросу). Это может немного упростить ваш код.

13 голосов
/ 17 мая 2010

Список → Набор → Список (отличный)

Просто добавьте все свои элементы в Set: это не позволяет повторять его элементы. Если вам нужен список впоследствии, используйте новый конструктор ArrayList(theSet) (где theSet - ваш результирующий набор).

9 голосов
/ 17 мая 2010

Я подозреваю, что Customer.equals() не реализован должным образом (или вообще не реализован).

List.contains() использует equals(), чтобы проверить, идентичен ли какой-либо из его элементов объекту, переданному в качестве параметра. Однако реализация по умолчанию equals проверяет физическую идентичность, а не значение идентичности. Поэтому, если вы не перезаписали его в Customer, он вернет false для двух отдельных объектов Customer, имеющих одинаковое состояние.

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

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

5 голосов
/ 24 июня 2015
private void removeTheDuplicates(List<Customer>myList) {
    for(ListIterator<Customer>iterator = myList.listIterator(); iterator.hasNext();) {
        Customer customer = iterator.next();
        if(Collections.frequency(myList, customer) > 1) {
            iterator.remove();
        }
    }
    System.out.println(myList.toString());

}
5 голосов
/ 17 мая 2010

Метод «Содержит» искал, содержит ли список запись, которая возвращает true из Customer.equals (Object o). Если вы не переопределили equals (Object) в Customer или одном из его родителей, он будет искать только существующее вхождение того же объекта. Может быть, это было то, что вы хотели, и в этом случае ваш код должен работать. Но если вы искали, чтобы не было двух объектов, представляющих одного и того же клиента, вам нужно переопределить функцию equals (Object), чтобы вернуть true, когда это так.

Также верно, что использование одной из реализаций Set вместо List даст вам удаление дубликатов автоматически и быстрее (для всего, кроме очень маленьких списков). Вам все равно нужно будет предоставить код для равных.

Вы также должны переопределить hashCode (), когда переопределяете equals ().

3 голосов
/ 31 июля 2015

Почти все вышеприведенные ответы верны, но я предлагаю использовать Карту или Набор при создании связанного списка, а не после, чтобы повысить производительность. Потому что преобразование списка в набор или карту, а затем повторное преобразование его в список - тривиальная работа.

Пример кода:

Set<String> stringsSet = new LinkedHashSet<String>();//A Linked hash set 
//prevents the adding order of the elements
for (String string: stringsList) {
    stringsSet.add(string);
}
return new ArrayList<String>(stringsSet);
3 голосов
/ 17 мая 2010

Два предложения:

  • Используйте HashSet вместо ArrayList. Это значительно ускорит проверки содержимого (), если у вас длинный список

  • Убедитесь, что Customer.equals () и Customer.hashCode () реализованы правильно, то есть они должны основываться на комбинированных значениях базовых полей в объекте customer.

...