Найти в списке объекты, у которых некоторые атрибуты имеют одинаковые значения

Учитывая список объектов (все одного типа), как я могу убедиться, что он содержит только один элемент для каждого значения определенного атрибута, даже если equals() может возвращать false для таких элементов из-за проверки большего количества атрибутов? В коде:

private void example() {
    List<SomeType> listWithDuplicates = new ArrayList<SomeType>();

    /*
     * create the "duplicate" objects. Note that both attributes passed to 
     * the constructor are used in equals(), though for the purpose of this 
     * question they are considered equal if the first argument was equal
     */
    SomeType someObject1 = new SomeObject1("hello", "1");
    SomeType someObject2 = new SomeObject1("hello", "2");

    List<SomeType> listWithoutDuplicates = removeDuplicates(listWithDuplicates)
    //listWithoutDuplicates should not contain someObject2
}

private List<SomeType> removeDuplicates(List<SomeType> listWithDuplicates) {
    /*
     * remove all but the first entry in the list where the first constructor-
     * arg was the same
     */
}

person Thomas Lötzer    schedule 06.01.2010    source источник


Ответы (4)


Можно использовать Set в качестве промежуточного заполнителя для поиска дубликатов, как предложил Божо. Вот пример реализации removeDuplicates().

private List<SomeType> removeDuplicates(List<SomeType> listWithDuplicates) {
    /* Set of all attributes seen so far */
    Set<AttributeType> attributes = new HashSet<AttributeType>();
    /* All confirmed duplicates go in here */
    List duplicates = new ArrayList<SomeType>();

    for(SomeType x : listWithDuplicates) {
        if(attributes.contains(x.firstAttribute())) {
            duplicates.add(x);
        }
        attributes.add(x.firstAttribute());
    }
    /* Clean list without any dups */
    return listWithDuplicates.removeAll(duplicates);
}
person Anurag    schedule 06.01.2010

Возможно, HashMap можно использовать так:

  private List<SomeType> removeDuplicates(List<SomeType> listWithDuplicates) {
   /*
   * remove all but the first entry in the list where the first constructor-
   * arg was the same
   */
   Iterator<SomeType> iter = listWithDuplicates.iterator();
   Map<String, SomeType> map = new HashMap<String, SomeType>();
   while(iter.hasnext()){
         SomeType i = iter.next();
         if(!map.containsKey(i.getAttribute())){
             map.put(i.getAttribute(), i);
         }
   }
   //At this point the map.values() is a collection of objects that are not duplicates.



  }
person Vincent Ramdhanie    schedule 06.01.2010
comment
Вероятно, сработает, но я потеряю порядок в списке. Я должен проверить, является ли это проблемой в моем случае. - person Thomas Lötzer; 06.01.2010

Если бы equals() были подходящими, я мог бы порекомендовать некоторые «стандартные» классы/методы коллекций. Как бы то ни было, я думаю, что ваш единственный вариант будет либо

  • копировать каждый элемент в другой список после предварительной проверки всех предшествующих элементов в исходном списке на наличие дубликатов; или

  • удалите из своего списка любой элемент, для которого вы нашли дубликат в предыдущем месте. Для удаления в списке лучше всего использовать LinkedList, где удаление не так дорого.

В любом случае проверка дубликатов, увы, будет операцией O(n^2).


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

person Carl Smotricz    schedule 06.01.2010

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

Связанный вопрос: как лучше всего сравнить два Коллекции в Java и работа с ними?

person Ben    schedule 06.01.2010