Как удалить дубликаты из списка?
Я хочу удалить дубликаты из списка, но то, что я делаю, не работает:
List<Customer> listCustomer = new ArrayList<Customer>();
for (Customer customer: tmpListCustomer)
{
if (!listCustomer.contains(customer))
{
listCustomer.add(customer);
}
}
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
: он не позволяет повторять его элементы. Если вам нужен список после этого, используйте newArrayList(theSet)
конструктор после этого (гдеtheSet
ваш результирующий набор).
Я подозреваю, что вы не могли бы
Customer.equals()
реализовано правильно (или вообще).
List.contains()
используетequals()
чтобы проверить, идентичен ли какой-либо из его элементов объекту, переданному в качестве параметра. Однако, реализация по умолчаниюequals
тесты на физическую идентичность, а не идентичность значения. Так что если вы не перезаписали его вCustomer
, он вернет false для двух различных объектов клиента, имеющих одинаковое состояние.вот мелкие детали как для реализации
equals
(иhashCode
, который является его парой-вы должны практически всегда реализовать оба, если вам нужно реализовать любой из них). Поскольку вы не показали нам класс клиента, трудно дать более конкретный совет.как отмечали другие, вам лучше использовать набор, а не выполнять работу вручную, но даже для этого вам все равно нужно реализовать эти методы.
метод "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);
выход:
перед значениями: [один, один, два]
после значений : [раз, два]