Каков наилучший способ удаления дубликатов в массиве в Java?
У меня есть массив объектов, для которого дубликаты удаляются / фильтруются. Я собирался просто переопределить equals & hachCode на элементах Object, а затем вставить их в Set … но я решил, что должен по крайней мере опросить stackoverflow, чтобы узнать, есть ли другой способ, возможно, какой-нибудь умный метод какого-либо другого API?
- Каковы правила для порядка оценки в Java?
- Существуют ли какие-либо виртуальные машины Java, которые могут сохранять свое состояние в файле, а затем перезагрузить это состояние?
- Где документация для метода values () для Enum?
- Скомпилируйте код полностью в памяти с помощью javax.tools.JavaCompiler
- Java 8: Разница между ссылкой метода Bound Receiver и UnBound Receiver
- Самый быстрый способ перебрать все символы в строке
- Создание XML с использованием SAX и Java
- Как мне избежать строк в JSON?
Я бы согласился с вашим подходом переопределить hashCode()
и equals()
и использовать то, что реализует Set
.
Это также делает абсолютно ясным для любых других разработчиков, что требуется недвойственная характеристика.
Другая причина – теперь вы можете выбрать реализацию, которая наилучшим образом отвечает вашим потребностям:
- HashSet
- TreeSet
- LinkedHashSet
и вам не нужно менять свой код, чтобы изменить реализацию в будущем.
Я нашел это в Интернете
Вот два метода, которые позволяют удалять дубликаты в 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); }
Первой мыслью было переопределение equals
и hashCode
и создание набора. Хорошая практика – иметь некоторую переопределенную версию этих методов в любом случае в вашей иерархии наследования.
Я думаю, что если вы используете LinkedHashSet
вы даже сохраните порядок уникальных элементов …
В принципе, вам нужна реализация LinkedHashSet
которая поддерживает интерфейс List
для произвольного доступа. Следовательно, это то, что вам нужно:
public class LinkedHashSetList
extends LinkedHashSet implements List {
// Implementations for List
methods here ...
}
Реализация методов List
позволит получить доступ к базовому LinkedHashSet
и манипулировать им. Хитрость заключается в том, чтобы этот class вел себя правильно, когда пытались добавить дубликаты с помощью методов List
add (исключение или повторное добавление элемента в другом индексе были бы параметрами: вы можете либо выбрать один из них, либо сделать настраиваемый пользователями classа).
Я хотел бы повторить точку зрения Джейсона в комментариях:
Зачем ставить себя на этот счет?
Зачем использовать массив для структуры данных, в которой не должно быть дубликатов?
Используйте Set
или SortedSet
(когда элементы также имеют естественный порядок), чтобы удерживать элементы. Если вам нужно сохранить порядок вставки, вы можете использовать LinkedHashSet
как было указано.
Для того, чтобы постобработать некоторую структуру данных, часто есть намек на то, что вы должны выбрать другую для начала.
Конечно, исходный пост задает вопрос: «Как вы получили этот массив (который мог содержать дублированные записи) в первую очередь?»
Вам нужен массив (с дубликатами) для других целей или вы можете просто использовать Set с самого начала?
В качестве альтернативы, если вам нужно знать количество вхождений каждого значения, вы можете использовать Map
для отслеживания подсчетов. Кроме того, определение classов Multimap в Google Collections может быть полезным.
Используйте List toRemove
для записи элемента в первый раз, когда iterator
наткнется на него, а затем, когда снова встретите записанный элемент, удалите его с помощью iterator.remove()
private void removeDups (Список списка) { Список toRemove = новый ArrayList (); for (Итератор it = list.iterator (); it.hasNext ();) { Объект next = it.next (); if (! toRemove.contains (next)) { toRemove.add (далее); } else { it.remove (); } } toremove.clear (); }
Set
, безусловно, лучший выбор. Единственный способ удалить вещи из массива (без создания нового) – это их исключить, а затем вы получите много нулевых проверок позже.
Говоря из общего стандарта программирования, вы всегда можете удвоить перечисление коллекций, а затем сравнить источник и цель.
И если ваше внутреннее перечисление всегда запускает одну запись после источника, это довольно эффективно (псевдокод следует следовать)
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]); } } }
Вы могли бы, возможно, добавить перерыв; после уничтожения, но тогда вы обнаружите только первый дубликат, но если это все, что вы когда-либо имели, то это была бы небольшая оптимизация.