J'ai une liste/collection d'objets qui peuvent ou non avoir les mêmes valeurs de propriété. Quelle est la manière la plus simple d'obtenir une liste distincte d'objets ayant des propriétés égales? Un type de collection convient-il le mieux à cette fin? Par exemple, en C #, je pourrais faire quelque chose comme ceci avec LINQ.
var recipients = (from recipient in recipientList
select recipient).Distinct();
Ma pensée initiale était d'utiliser lambdaj ( texte du lien ), mais il ne semble pas le supporter.
Utilisez une implémentation de l'interface Set<T>
(La classe T peut avoir besoin d'une méthode .equals()
personnalisée, et vous devrez peut-être implémenter cette .equals()
vous-même). Généralement, un HashSet
le fait immédiatement: il utilise la méthode Object.hashCode()
et Object.equals()
pour comparer les objets. Cela devrait être assez unique pour les objets simples. Sinon, vous devrez implémenter T.equals()
et T.hashCode()
en conséquence.
Voir le commentaire de Gaurav Saini ci-dessous pour les bibliothèques aidant à implémenter equals et hashcode.
return new ArrayList(new HashSet(recipients));
Placez-les dans un TreeSet qui contient un comparateur personnalisé, qui vérifie les propriétés dont vous avez besoin:
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
Java 8:
recipients = recipients.stream()
.distinct()
.collect(Collectors.toList());
ordre préservant la version de la réponse ci-dessus
return new ArrayList(new LinkedHashSet(recipients));
Si vous utilisez Collections Eclipse , vous pouvez utiliser la méthode distinct()
.
ListIterable<Integer> integers = Lists.mutable.with(1, 3, 1, 2, 2, 1);
Assert.assertEquals(
Lists.mutable.with(1, 3, 2),
integers.distinct());
L'avantage d'utiliser distinct()
au lieu de convertir en ensemble puis de revenir en liste est que distinct()
préserve l'ordre de la liste d'origine, en conservant la première occurrence de chaque élément. Il est implémenté en utilisant à la fois un ensemble et une liste.
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;
Si vous ne pouvez pas convertir votre liste d'origine en un type de collections Eclipse, vous pouvez utiliser ListAdapter
pour obtenir la même API.
MutableList<Integer> distinct = ListAdapter.adapt(integers).distinct();
Remarque: je suis un committer pour les collections Eclipse.
Vous pouvez utiliser un Set
. Il y a quelques implémentations:
HashSet
utilise hashCode
et equals
d'un objet.TreeSet
utilise compareTo
(défini par Comparable
) ou compare
(défini par Comparator
). Gardez à l'esprit que la comparaison doit être cohérente avec equals
. Voir TreeSet
JavaDocs pour plus d'informations.Gardez également à l'esprit que si vous remplacez equals
, vous devez remplacer hashCode
de sorte que deux objets égaux aient le même code de hachage.
La façon ordinaire de procéder serait de convertir en un ensemble, puis de revenir à une liste. Mais vous pouvez vous faire plaisir avec Java fonctionnel . Si vous avez aimé Lamdaj, vous allez adorer FJ.
recipients = recipients
.sort(recipientOrd)
.group(recipientOrd.equal())
.map(List.<Recipient>head_());
Vous devrez avoir défini un ordre pour les destinataires, recipientOrd
. Quelque chose comme:
Ord<Recipient> recipientOrd = ord(new F2<Recipient, Recipient, Ordering>() {
public Ordering f(Recipient r1, Recipient r2) {
return stringOrd.compare(r1.getEmailAddress(), r2.getEmailAddress());
}
});
Fonctionne même si vous n'avez pas le contrôle de equals()
et hashCode()
sur la classe Recipient.
En fait, lambdaj implémente cette fonctionnalité via la méthode selectDistinctArgument