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

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


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


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

7 ответов


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

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

если ваш критерий зависит от контекста, org.апаш.палата общин.коллекции.CollectionUtils.выберите(Ява.утиль.Коллекция, орг.апаш.палата общин.коллекции.Сказуемое) может быть правильным решением для вас.


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

вот эскиз (не проверен):

   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!
      }
   }

Edit: из вашего комментария, вы просто хотите знать, если есть are дубликаты. Подход выше работает для этого тоже. Но вы могли бы просто создать java.утиль.SortedSet с пользовательским компаратором. Вот набросок:

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

вы можете адаптировать набор 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 [ссылка].


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

для получения дополнительной информации см. здесь:поиск дубликатов в коллекции


Я создал новый интерфейс, похожий на 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;
}

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


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.

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


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