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


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

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

16 ответов:

если этот код не работает, вы, вероятно, не реализовали 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 для выполнения проверки дубликатов.

Если вы хотите сохранить текущий порядок и не нужен Set, пожалуй, самый простой-это:

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

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

Set<Customer> depdupeCustomers = new LinkedHashSet<>(customers);
customers.clear();
customers.addAll(dedupeCustomers);

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

Arrays.stream(yourArray).distinct()
                    .collect(Collectors.toList());

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

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

Примечание: не забудьте переопределить hashCode() Если вы собираетесь переопределить equals()! В противном случае у вас могут возникнуть проблемы с вашими хэш-картами и другими структурами данных. Для хорошего освещения того, почему это так и каких ловушек следует избегать, подумайте о том, чтобы взглянуть на Джоша Блоха Эффективная 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, но это не совсем по вашему вопросу). Это может немного упростить ваш код.

List → Set → List (distinct)

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

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

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

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

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

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

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

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

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());

}

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

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

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

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

Пример Кода:

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);

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

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

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

самый чистый способ-это:

List<XXX> lstConsultada = dao.findByPropertyList(YYY);
List<XXX> lstFinal = new ArrayList<XXX>(new LinkedHashSet<GrupoOrigen>(XXX));

и заменить hascode и equals над свойствами идентификатора каждого объекта

ИМХО лучший способ, как это сделать в эти дни:

Предположим, у вас есть коллекция "dups " и вы хотите создать другую коллекцию, содержащую те же элементы, но со всеми дубликатами устранены. Следующий однострочный делает трюк.

Collection<collectionType> noDups = new HashSet<collectionType>(dups);

Она работает путем создания набора, который, по определению, не может содержать дубликаты.

на основе oracle doc.

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

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

Otherise просто использовать Set исполнение HashSet,TreeSet непосредственно и пропустить List этап строительства.

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

использование Java 8 stream api.

    List<String> list = new ArrayList<>();
    list.add("one");
    list.add("one");
    list.add("two");
    System.out.println(list);
    Collection<String> c = list.stream().collect(Collectors.toSet());
    System.out.println(c);

выход:

перед значениями: [один, один, два]

после значений : [раз, два]

Class removeduplicates 
{
    public static void main(string args[[])
    {   
        int I;
        for(int =0;i'<10;I++)
        {
            system.out.println(+i);
            if([]I=[j])
            {
                system.out.println(1,2,3,1,1,1,2,2,2)
            }
        }
    }
}