2010-05-17 2 views
50

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

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

вы не должны писать код, как это в Java, так же, как вы не должны писать свои собственные процедуры сортировки в Java. Если у него есть возможность сделать это через нечто вроде Set, используйте это. –

ответ

44

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

8

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

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

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

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

+0

как я могу это реализовать? – Mercer

+0

Переопределить методы equals и hashCode из java.lang.Object. Вы захотите прочитать следующее: http://java.sun.com/developer/Books/effectivejava/Chapter3.pdf – duffymo

+0

правильный способ удаления дубликатов из списка на Java - использовать Set. И вы не можете просто переопределить equals() без переопределения hashCode(). –

12

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

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

+3

Используя Set, вы получите точно такие же результаты, как и код, написанный выше, только быстрее. Плакат говорит, что «не работает» не «работает слишком медленно». – DJClayworth

+0

well Set работает, и его код не подходит для лучшего рабочего кода, и вам не нужно писать или ошибочный код, который вы действительно не понимаете и не работает. –

+1

Я думаю, вы предполагаете, что он хочет только удалить дубликаты ссылок на один и тот же объект. Если это так, то опубликованный код будет работать. – DJClayworth

0

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

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

Otherise просто использовать SetHashSet, реализации внешних TreeSet непосредственно и пропустить этап List строительства.

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

+0

Набор будет делать то же самое, что и опубликованный код, только быстрее. – DJClayworth

+0

Скорость не такая важная, как ремонтопригодность, вам не нужно поддерживать код для Set, и это самодокументирование и правильная идиома Java. –

+4

Гомер: Вы можете сделать это правильно, по-другому или по пути максимальной мощности. Барт: Каков способ максимальной мощности? Гомер: Это неправильно, только быстрее. – DJClayworth

1

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

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

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

3

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

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

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

5

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

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

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

+0

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

+0

Вы имеете в виду предложение, которое совпадает с принятым ответом? – DJClayworth

+1

@DJClayworth: После того, как вы прочли свой пост более внимательно, я согласен с тем, что это абсолютно правильно (во время моего первого чтения, хотя вы предложили сделать «специальный случай» равным). Вы получаете мой +1 за несправедливое downvoting. С другой стороны, глядя на другие сообщения здесь, кто-то был на уклоне от лихорадки. –

13

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

+3

+1 для запоминания того, что Set нельзя использовать, если вам нужно поддерживать порядок. – DJClayworth

+0

для предложения 'TreeSet <>()' для поддержания порядка: +1 – Thomas

84

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

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

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

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

Если вы не переопределили метод equals (Customer's) equals, HashSet будет сравнивать местоположение объектов в памяти, чтобы они не были равны и вы все равно будете иметь дубликаты в своем новом наборе. –

+2

@GinjaNinja Существует неявное предположение, что 'equals' (и' hashCode') реализовано способом, который имеет смысл для типа. Например, 'LinkedHashSet ' будет удалять только дубликаты, которые были точно таким же объектом, потому что это означает, что для экземпляров 'JWindow' равны. –

+0

лучший ответ; работал. –

1

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

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

a й переопределить hascode и equals над свойства корочки каждого объекта

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

} 
3

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

Пример кода:

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

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

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

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

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

Основано на документе оракула.

+0

Чтобы добавить к этому ответу, использование 'Set' также уничтожает кучу места, чего здесь можно избежать. –

17

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

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

Лучшее решение. С помощью набора вы теряете заказ (который может быть требованием или нет, но с этим решением вам не нужно об этом думать). – cocorossello

+2

Вы также можете использовать TreeSet, но тогда вам нужно реализовать Comparable, что не всегда желательно – cocorossello

+0

Как бы я обошел «Тип несоответствия: невозможно преобразовать из списка в список ». Ошибка? –

-2
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) 
      } 
     } 
    } 
} 
1

Использование Java 8 потока 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); 

Выход:

До значений: [один, один, два]

После значения: [один, два]

Смежные вопросы