Поиск дубликатов записей в коллекции

Есть ли инструмент или библиотека для поиска дубликатов записей в коллекции в соответствии с конкретными критериями, которые могут быть реализованы?


Чтобы я убедился: я хочу сравнивать записи друг с другом в соответствии с конкретными критериями. Поэтому я думаю, что Predicate, возвращающий только true или false, недостаточно.


Я не могу использовать equals.

Ответ 1

Я создал новый интерфейс, похожий на IEqualityComparer<T> интерфейс в .NET.

Такой EqualityComparator<T> я затем переходит к следующему методу, который обнаруживает дубликаты.

public static <T> boolean hasDuplicates(Collection<T> collection,
        EqualsComparator<T> equalsComparator) {
    List<T> list = new ArrayList<>(collection);
    for (int i = 0; i < list.size(); i++) {
        T object1 = list.get(i);
        for (int j = (i + 1); j < list.size(); j++) {
            T object2 = list.get(j);
            if (object1 == object2
                    || equalsComparator.equals(object1, object2)) {
                return true;
            }
        }
    }
    return false;
}

Таким образом, я могу настроить сравнение с моими потребностями.

Ответ 2

Это зависит от семантики критерия:

Если ваш критерий всегда один и тот же для данного класса и , свойственный базовой концепции, вы должны просто реализовать equals и hashCode и использовать набор.

Если ваш критерий зависит от контекста, org.apache.commons.collections.CollectionUtils.select(java.util.Collection, org.apache.commons.collections.Predicate) может быть правильным решением для вас.

Ответ 3

Если вы хотите найти дубликаты, а не просто удалить их, одним из подходов было бы бросить коллекцию в массив, отсортировать массив через Comparator, который реализует ваши критерии, а затем линейно перемещаться по массиву, ища соседние дубликаты.

Здесь эскиз (не проверен):

   MyComparator myComparator = new MyComparator();
   MyType[] myArray = myList.toArray();
   Arrays.sort( myArray, myComparator );
   for ( int i = 1; i < myArray.length; ++i ) {
      if ( 0 == myComparator.compare( myArray[i - 1], myArray[i] )) {
         // Found a duplicate!
      }
   }

Изменить: Из вашего комментария вы просто хотите узнать, есть ли дубликаты. Этот подход тоже работает для этого. Но вы могли бы просто создать java.util.SortedSet с помощью специального Компаратора. Здесь эскиз:

   MyComparator myComparator = new MyComparator();
   TreeSet treeSet = new TreeSet( myComparator );
   treeSet.addAll( myCollection );
   boolean containsDuplicates = (treeSet.size() != myCollection.size()); 

Ответ 4

Вы можете адаптировать набор Java для поиска дубликатов среди объектов произвольного типа: оберните свой целевой класс в приватную оболочку, которая оценивает равенство на основе ваших критериев и создаст набор оберток.

Вот несколько длинный пример, иллюстрирующий эту технику. Он считает двух людей с одинаковым первым именем равными, и поэтому он обнаруживает три дубликата в массиве из пяти объектов.

import java.util.*;
import java.lang.*;

class Main {
    static class Person {
        private String first;
        private String last;
        public String getFirst() {return first;}
        public String getLast() {return last;}
        public Person(String f, String l) {
            first = f;
            last = l;
        }
        public String toString() {
            return first+" "+last;
        }
    }
    public static void main (String[] args) throws java.lang.Exception {
        List<Person> people = new ArrayList<Person>();
        people.add(new Person("John", "Smith"));
        people.add(new Person("John", "Scott"));
        people.add(new Person("Jack", "First"));
        people.add(new Person("John", "Walker"));
        people.add(new Person("Jack", "Black"));
        Set<Object> seen = new HashSet<Object>();
        for (Person p : people) {
            final Person thisPerson = p;
            class Wrap {
                public int hashCode() { return thisPerson.getFirst().hashCode(); }
                public boolean equals(Object o) {
                    Wrap other = (Wrap)o;
                    return other.wrapped().getFirst().equals(thisPerson.getFirst());
                }
                public Person wrapped() { return thisPerson; }
            };
            Wrap wrap = new Wrap();
            if (seen.add(wrap)) {
                System.out.println(p + " is new");
            } else {
                System.out.println(p + " is a duplicate");
            }
        }
    }
}

Вы можете играть с этим примером на ideone [link].

Ответ 5

Вы можете использовать карту и, итерации по коллекции, поместите элементы в карту (предикаты будут формировать ключ), и если уже есть запись, вы нашли дубликат.

Подробнее см. здесь: Поиск дубликатов в коллекции

Ответ 6

Treeset позволяет сделать это легко:

Set uniqueItems = new TreeSet<>(yourComparator);
List<?> duplicates = objects.stream().filter(o -> !uniqueItems.add(o)).collect(Collectors.toList());

yourComarator используется при вызове uniqueItems.add(o), который добавляет элемент в набор и возвращает true, если элемент уникален. Если компаратор считает объект дубликатом, add(o) вернет false.

Обратите внимание, что метод item equals должен соответствовать yourComarator согласно документации по TreeSet, чтобы это работало.

Ответ 7

Итерировать ArrayList, который содержит дубликаты и добавить их в HashSet. Когда метод add возвращает false в HashSet, просто зарегистрируйте дубликат на консоли.