У меня есть массив объектов, дубликаты которых нужно удалить / отфильтровать. Я собирался просто переопределить equals & hachCode в элементах Object, а затем вставить их в Set ... но я решил, что должен хотя бы опросить stackoverflow, чтобы узнать, есть ли другой способ, возможно, какой-то умный метод какого-то другого API?
Задайте вопрос об удалении дубликатов ... получите кучу повторяющихся ответов. Ирония!
То, как вы описываете, идеально.




Я согласен с вашим подходом к переопределению hashCode() и equals() и использованию чего-то, что реализует Set.
Это также делает абсолютно ясным для всех других разработчиков, что требуется не дублирующая характеристика.
Еще одна причина - теперь вы можете выбрать реализацию, которая лучше всего соответствует вашим потребностям:
и вам не нужно менять свой код, чтобы изменить реализацию в будущем.
Set определенно ваш лучший выбор. Единственный способ удалить что-либо из массива (без создания нового) - это обнулить их, а затем вы получите множество проверок на null позже.
Моей первой мыслью тоже было переопределение equals и hashCode и создание набора. В любом случае рекомендуется иметь в иерархии наследования некоторую переопределенную версию этих методов.
Я считать, что если вы используете LinkedHashSet, вы даже сохраните порядок уникальных элементов ...
Да, LinkedHashSet сохранит порядок вставки.
Не рекомендуется переопределять equals и hashCode «в любом случае», особенно в любом классе, который будет находиться в иерархии наследования. См. Эффективная Java (Блох) для получения дополнительной информации.
Макдауэлл, я не совсем понял - я имел в виду, что в вашей иерархии наследования должна быть переопределенная версия где-то, и я изменил ответ, чтобы отразить это. У меня нет копии «Эффективной Java» - это к чему Блох?
Я нашел это в сети
Вот два метода, которые позволяют удалять дубликаты в ArrayList. removeDuplicate не поддерживает порядок, в то время как removeDuplicateWithOrder поддерживает порядок с некоторыми издержками производительности.
Метод removeDuplicate:
/** List order not maintained **/
public static void removeDuplicate(ArrayList arlList)
{
HashSet h = new HashSet(arlList);
arlList.clear();
arlList.addAll(h);
}
Метод removeDuplicateWithOrder:
/** List order maintained **/
public static void removeDuplicateWithOrder(ArrayList arlList)
{
Set set = new HashSet();
List newList = new ArrayList();
for (Iterator iter = arlList.iterator(); iter.hasNext();) {
Object element = iter.next();
if (set.add(element))
newList.add(element);
}
arlList.clear();
arlList.addAll(newList);
}
Хороший ответ, но ваш второй пример не находится в блоке формата кода
спасибо Ken G ... я пробовал это пару раз, но я не смог исправить второй блог кода
LinkedHashSet держит его в порядке. Это может еще больше его оптимизировать.
Исходя из общего стандарта программирования, вы всегда можете дважды перечислить коллекции, а затем сравнить источник и цель.
И если ваше внутреннее перечисление всегда начинается с одной записи после источника, это довольно эффективно (псевдокод для подражания)
foreach ( array as source )
{
// keep track where we are in the array
place++;
// loop the array starting at the entry AFTER the current one we are comparing to
for ( i=place+1; i < max(array); i++ )
{
if ( source === array[place] )
{
destroy(array[i]);
}
}
}
Возможно, вы могли бы добавить перерыв; после уничтожения, но тогда вы обнаружите только первый дубликат, но если это все, что у вас когда-либо будет, тогда это будет хорошая небольшая оптимизация.
Я хотел бы повторить мысль, высказанную Джейсоном в комментариях:
Зачем вообще ставить себя в эту точку?
Зачем использовать массив для структуры данных, в которой вообще не должно быть дубликатов?
Всегда используйте Set или SortedSet (когда элементы тоже имеют естественный порядок), чтобы удерживать элементы. Если вам нужно сохранить порядок вставки, вы можете использовать LinkedHashSet, как было указано.
Необходимость постобработки некоторой структуры данных часто является намеком на то, что вы должны были выбрать для начала другую.
Я согласен со всеми комментариями по поводу того, что исходная структура данных является массивом. Я пытаюсь убедить разработчика провести рефакторинг до Set. Спасибо всем за отзывы и мудрость!
Конечно, в исходном сообщении возникает вопрос: «Как вы вообще получили этот массив (который может содержать повторяющиеся записи)?»
Вам нужен массив (с дубликатами) для других целей, или вы могли бы просто использовать Set с самого начала?
В качестве альтернативы, если вам нужно знать количество появлений каждого значения, вы можете использовать Map<CustomObject, Integer> для отслеживания счетчиков. Также может оказаться полезным определение Коллекции Google классов Multimap.
По сути, вам нужна реализация LinkedHashSet<T>, которая поддерживает интерфейс List<T> для произвольного доступа. Следовательно, это то, что вам нужно:
public class LinkedHashSetList<T> extends LinkedHashSet<T> implements List<T> {
// Implementations for List<T> methods here...
}
Реализация методов List<T> будет обращаться к лежащему в основе LinkedHashSet<T> и манипулировать им. Хитрость заключается в том, чтобы этот класс вел себя правильно, когда кто-то пытается добавить дубликаты с помощью методов добавления List<T> (выбрасывание исключения или повторное добавление элемента в другой индекс будут вариантами: которые вы можете либо выбрать один из, либо сделать настраиваемым пользователями класса).
Используйте список distinctList для записи элемента, когда iterator впервые сталкивается с ним, возвращает отдельный список, поскольку список удаляет все дубликаты
private List removeDups(List list) {
Set tempSet = new HashSet();
List distinctList = new ArrayList();
for(Iterator it = list.iterator(); it.hasNext();) {
Object next = it.next();
if (tempSet.add(next)) {
distinctList.add(next);
}
}
return distinctList;
}
сложность очень высока, поскольку List.contains имеет временную сложность O (n), поэтому сложность составляет O (N ^ 2)
@FilipLuchianenco вы правы, я обновил свою реализацию
Затем вам нужно только продолжать добавлять новое значение, если оно существует, оно просто вернет false. В результате вы получите итератор и Set, в которые вы продолжаете добавлять уникальные значения. Единственным недостатком является порядок, поскольку Set не сохраняет его из-за изменения размера. Тогда другое решение состоит в том, чтобы иметь список и набор, и если ваш set.add (object) возвращает true, вы также добавляете его в новый список; затем верните список.
Почему нам нужно заботиться о порядке набора, поскольку мы собираемся изменить список, переданный в функции, который мы используем итератор для удаления дубликатов, который не меняет внутренний порядок списка
ну, удаление элемента из List очень неэффективно, поскольку List.remove () должен будет каждый раз создавать новый список и копировать все элементы, поэтому ваша сложность теперь составляет O (n ^ k), где k - размер списка . Так что я даже не хотел рассматривать это как вариант.
рефакторинг, хотя я сомневаюсь, что временная сложность была O (n ^ k), потому что временная сложность ArrayList.remove (i) равна n, тогда наихудший случай - O (n ^ 2).
выглядит отлично! Причина, по которой я сказал, что это n ^ k, состоит в том, что вы вызываете .remove k раз, где k - количество повторяющихся элементов. Так что это не n ^ 2 и не n ^ arraySize, как я уже говорил. В худшем случае, если все элементы одинаковы, то это будет n ^ arraySize. Кроме того, не забудьте упомянуть, что методы equals и hashCode должны быть переопределены и записаны правильно для используемых объектов, иначе он будет продолжать добавлять одни и те же элементы для установки, поскольку хэш-код будет отличаться для двух объектов с одинаковым содержимым, если не переопределить его.
да, OP упомянул, что он собирается переопределить equals и hashCode. Когда вы говорите n ^ arraySize, вы имеете в виду n * arraySize?
на самом деле вы правы, худший случай, учитывая, что решение было O (N ^ 2), теперь это O (N).
Зачем ставить себя в это место? Почему бы в первую очередь не предотвратить дублирование?