2009-06-19 4 views
19

У меня есть список/коллекция объектов, которые могут иметь или не иметь одинаковые значения свойств. Каков самый простой способ получить отдельный список объектов с равными свойствами? Является ли один тип коллекции наиболее подходящим для этой цели? Например, в C# я мог бы сделать что-то вроде LINQ.Java - отличительный список объектов

var recipients = (from recipient in recipientList 
       select recipient).Distinct(); 

Моя первая мысль была использовать lambdaj (link text), но он не появляется, чтобы поддержать это.

+1

Используйте эту библиотеку LINQ (github.com/nicholas22/jpropel -light) и выполните список recipientList; recipientList.distinct(); Это именно то, что делает ваш код. –

ответ

28

Используйте реализацию интерфейса Set<T> (для класса T может потребоваться специальный метод .equals(), и вам, возможно, придется реализовать это .equals()). Обычно HashSet делает это из коробки: для сравнения объектов используется метод Object.hashCode() и Object.equals(). Это должно быть достаточно уникальным для простых объектов. В противном случае вам придется реализовать T.equals() и T.hashCode() соответственно.

См. Комментарий Gaurav Saini ниже для библиотек, помогающих реализовать равные и хэш-коды.

+1

HashSet также использует equals, если есть хеш-столкновение. –

+1

Это неверно. Object.hashCode() проверяет подлинность, а не значимое равенство. Для 2 объектов - разные ссылки - которые в значительной степени равны Object.hashCode() вернет false. Всегда используйте hashCode() и equals() для объектов, которые будут использоваться в наборах или как ключи в Картах. –

+0

Не совсем правильно. Позвольте мне привести вам пример: если hashCode() возвращает 1 (совершенно законный), то это приводит к столкновению хэша, и поэтому equals будет вызываться HashSet (на самом деле он поддерживается HashMap). –

20

Поместите их в TreeSet, который имеет собственный компаратор, который проверяет свойства, нужно:

SortedSet<MyObject> set = new TreeSet<MyObject>(new Comparator<MyObject>(){ 

    public int compare(MyObject o1, MyObject o2) { 
     // return 0 if objects are equal in terms of your properties 
    } 
}); 

set.addAll(myList); // eliminate duplicates 
+3

Работает только в том случае, если компаратор совместим с equals(), и в этот момент вам будет лучше с HashSet. –

+2

Почему это должно быть согласовано с equals()? Это обычно так, но на данный момент нам нужно удалить некоторые дубликаты на основе пользовательского условия. Компаратор - самый ненавязчивый способ, о котором я могу думать. –

+1

Интересно, что документы TreeSet указывают на наличие проблем, если Comparator несовместим с equals(), в то время как документы для TreeMap (на которых основан TreeSet) просто говорят, что equals() будет проигнорирован в этом случае. Теперь я думаю, что это лучший ответ. +1 –

4

Вы можете использовать Set. Там в нескольких реализациях:

  • HashSet использует объект hashCode и equals.
  • TreeSetcompareTo (определено Comparable) или compare (определено Comparator). Имейте в виду, что сравнение должно соответствовать equals. См. TreeSet JavaDocs для получения дополнительной информации.

Также имейте в виду, что если переопределить equals необходимо переопределить hashCode таким образом, что два равных объектов имеет один и тот же хэш-код.

3

Обычным способом сделать это было бы преобразование в набор, а затем обратно в список. Но вы можете получить фантазию с Functional Java. Если вам понравился Lamdaj, вы полюбите FJ.

recipients = recipients 
      .sort(recipientOrd) 
      .group(recipientOrd.equal()) 
      .map(List.<Recipient>head_()); 

Вам необходимо определили порядок получателей, recipientOrd. Что-то вроде:

Ord<Recipient> recipientOrd = ord(new F2<Recipient, Recipient, Ordering>() { 
    public Ordering f(Recipient r1, Recipient r2) { 
    return stringOrd.compare(r1.getEmailAddress(), r2.getEmailAddress()); 
    } 
}); 

работает, даже если вы не имеете контроль equals() и hashCode() на классе получателя.

+0

Зачем нужно добавлять заказы для объектов? – javacavaj

+1

Вам нужен заказ, чтобы метод сортировки знал, как их упорядочить. – Apocalisp

31
return new ArrayList(new HashSet(recipients)); 
+1

На самом деле вы ответили на вопрос (вместо простого перечисления инструментов для ответа на вопрос). – orbfish

+0

Ха, не передумал использовать этот быстрый «трюк», чтобы получить уникальный список предметов :) – Bogdan

+0

Это лучший ответ .. Thanx :) –

5

сохраняющей версию выше ответа

return new ArrayList(new LinkedHashSet(recipients)); 
7

Если вы используете Eclipse Collections, вы можете использовать метод distinct().

ListIterable<Integer> integers = Lists.mutable.with(1, 3, 1, 2, 2, 1); 
Assert.assertEquals(
    Lists.mutable.with(1, 3, 2), 
    integers.distinct()); 

Преимущество использования distinct() вместо преобразования в набор, а затем обратно в список, что distinct() сохраняет порядок первоначального списка, сохраняя при этом первое вхождение каждого элемента. Он реализован с использованием набора и списка.

MutableSet<T> seenSoFar = Sets.mutable.with(); 
int size = list.size(); 
for (int i = 0; i < size; i++) 
{ 
    T item = list.get(i); 
    if (seenSoFar.add(item)) 
    { 
     targetCollection.add(item); 
    } 
} 
return targetCollection; 

Если вы не можете преобразовать исходный список в типа Eclipse, Collections, вы можете использовать ListAdapter, чтобы получить тот же API.

MutableList<Integer> distinct = ListAdapter.adapt(integers).distinct(); 

Примечание: Я являюсь коммиттером для коллекций Eclipse.

Смежные вопросы