Я хочу удалить дубликаты из списка, но что я делаю, не работает:
List<Customer> listCustomer = new ArrayList<Customer>();
for (Customer customer: tmpListCustomer)
{
if (!listCustomer.contains(customer))
{
listCustomer.add(customer);
}
}
Если этот код не работает, возможно, вы неправильно реализовали 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
для проверки дубликатов.
Правильный ответ для Java - использовать Set . Если у вас уже есть List
и вы хотите его дублировать
Set<Customer> s = new HashSet<Customer>(listCustomer);
Otherise, просто используйте реализацию Set
HashSet
, TreeSet
] и пропустите этап построения List
.
Вам нужно будет переопределить hashCode ()
и equals ()
в классах домена, которые помещены в Set
, а также убедиться, что поведение, которое вы хотите, на самом деле то, что вы получаете. equals ()
может быть столь же простым, как сравнение уникальных идентификаторов объектов, так и сложным, как сравнение каждого поля. hashCode ()
может быть таким же простым, как возвращение hashCode ()
уникального идентификатора ' String
представления или hashCode ()
.
Метод "содержит" искал, содержит ли список запись, которая возвращает истину из Customer.equals (объект o). Если вы не переопределили equals (Object) в Customer или одном из его родителей, тогда он будет искать только существующее вхождение того же объекта. Возможно, это было то, что вы хотели, и в этом случае ваш код должен работать. Но если вы искали, чтобы у вас не было двух объектов, представляющих одного и того же клиента, вам нужно переопределить equals (Object), чтобы вернуть true, когда это так.
Также верно, что использование одной из реализаций Set вместо List приведет к автоматическому и более быстрому удалению дубликатов (для всего, кроме очень маленьких списков). Вам все равно нужно будет предоставить код для равных.
Вы также должны переопределить hashCode () при переопределении equals ().
Просто добавьте все свои элементы в Набор
: это не позволяет его элементы повторяться. Если вам впоследствии понадобится список, используйте после этого конструктор new ArrayList (theSet)
(где theSet
- ваш результирующий набор).
Я подозреваю, что Customer.equals ()
у вас, возможно, не реализован должным образом (или вообще).
List.contains ()
использует equals ()
, чтобы проверить, идентичен ли какой-либо из его элементов объекту, переданному в качестве параметра. Однако реализация по умолчанию эквивалентна
тестам на физическую идентичность, а не на идентичность по ценности. Поэтому, если вы не перезаписали его в Customer
, он вернет false для двух разных объектов Customer, имеющих идентичное состояние.
Вот мельчайшие подробности из , как реализовать equals
(и hashCode
, который является его парой - вы должны практически всегда реализовывать оба, если вам нужно реализовать любой из них). Поскольку вы не показали нам класс «Клиент», вам сложно дать более конкретный совет.
Как отмечали другие, лучше использовать Set, чем выполнять работу вручную, но даже для этого вам все равно необходимо реализовать эти методы.
Реализует ли Заказчик контракт 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
, но это не совсем относится к вашему вопросу). Это может немного упростить ваш код.
Два предложения:
Используйте HashSet вместо ArrayList. Это значительно ускорит проверку contains (), если у вас длинный список
. Убедитесь, что Customer.equals () и Customer.hashCode () реализованы правильно, т.е. они должны основываться на комбинированных значениях базовых полей в объект клиента.
Как уже упоминалось, вы, вероятно, неправильно реализуете equals ().
Однако вы должны также отметить, что этот код считается довольно неэффективным, поскольку время выполнения может быть числом элементов в квадрате.
Возможно, вы захотите рассмотреть возможность использования структуры Set вместо List или сначала создать Set, а затем преобразовать его в список.