Как удалить дубликаты из списка?

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

List<Customer> listCustomer = new ArrayList<Customer>();    
for (Customer customer: tmpListCustomer)
{
  if (!listCustomer.contains(customer)) 
  {
    listCustomer.add(customer);
  }
 }
55
задан joragupra 6 January 2014 в 09:43
поделиться

8 ответов

Если этот код не работает, возможно, вы неправильно реализовали 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 , чтобы два Равные объекты клиентов возвращают одно и то же хеш-значение. Соответствующий hashCode для приведенного выше определения равняется :

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

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

48
ответ дан 26 November 2019 в 17:36
поделиться

Правильный ответ для Java - использовать Set . Если у вас уже есть List и вы хотите его дублировать

Set<Customer> s = new HashSet<Customer>(listCustomer);

Otherise, просто используйте реализацию Set HashSet , TreeSet ] и пропустите этап построения List .

Вам нужно будет переопределить hashCode () и equals () в классах домена, которые помещены в Set , а также убедиться, что поведение, которое вы хотите, на самом деле то, что вы получаете. equals () может быть столь же простым, как сравнение уникальных идентификаторов объектов, так и сложным, как сравнение каждого поля. hashCode () может быть таким же простым, как возвращение hashCode () уникального идентификатора ' String представления или hashCode () .

0
ответ дан 26 November 2019 в 17:36
поделиться

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

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

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

5
ответ дан 26 November 2019 в 17:36
поделиться

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

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

13
ответ дан 26 November 2019 в 17:36
поделиться

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

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

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

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

9
ответ дан 26 November 2019 в 17:36
поделиться

Реализует ли Заказчик контракт equals () ?

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

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

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

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

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

13
ответ дан 26 November 2019 в 17:36
поделиться

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

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

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

3
ответ дан 26 November 2019 в 17:36
поделиться

Как уже упоминалось, вы, вероятно, неправильно реализуете equals ().

Однако вы должны также отметить, что этот код считается довольно неэффективным, поскольку время выполнения может быть числом элементов в квадрате.

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

1
ответ дан 26 November 2019 в 17:36
поделиться
Другие вопросы по тегам:

Похожие вопросы: