Je veux supprimer les doublons d'une liste, mais ce que je fais ne fonctionne pas:
List<Customer> listCustomer = new ArrayList<Customer>();
for (Customer customer: tmpListCustomer)
{
if (!listCustomer.contains(customer))
{
listCustomer.add(customer);
}
}
Si ce code ne fonctionne pas, vous n'avez probablement pas implémenté equals(Object)
sur la classe Customer
de manière appropriée.
Vraisemblablement, il existe une clé (appelons-la customerId
) qui identifie de manière unique un client; par exemple.
class Customer {
private String customerId;
...
Une définition appropriée de equals(Object)
ressemblerait à ceci:
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (!(obj instanceof Customer)) {
return false;
}
Customer other = (Customer) obj;
return this.customerId.equals(other.customerId);
}
Pour être complet, vous devriez implémentez également hashCode
de sorte que deux objets Customer
égaux renvoient la même valeur de hachage. Une correspondance hashCode
pour la définition ci-dessus de equals
serait:
public int hashCode() {
return customerId.hashCode();
}
Il convient également de noter que ce n'est pas un moyen efficace de supprimer les doublons si la liste est longue. (Pour une liste avec N clients, vous devrez effectuer des comparaisons de N*(N-1)/2
dans le pire des cas; c'est-à-dire lorsqu'il n'y a pas de doublons.) Pour une solution plus efficace, utilisez quelque chose comme HashSet
pour effectuer la vérification des doublons.
En supposant que vous souhaitiez conserver la commande actuelle et que vous ne souhaitiez pas une Set
, le plus simple est peut-être:
List<Customer> depdupeCustomers =
new ArrayList<>(new LinkedHashSet<>(customers));
Si vous souhaitez modifier la liste d'origine:
Set<Customer> depdupeCustomers = new LinkedHashSet<>(customers);
customers.clear();
customers.addAll(dedupeCustomers);
Mise à jour Java 8
vous pouvez utiliser un flux de tableau comme ci-dessous:
Arrays.stream(yourArray).distinct()
.collect(Collectors.toList());
Le client met-il en œuvre le contrat equals()
?
S'il n'implémente pas equals()
et hashCode()
, alors listCustomer.contains(customer)
vérifiera si le même instance exacte instance existe déjà dans la liste (Par exemple, j'entends exactement le même objet - adresse mémoire, etc.). Si vous voulez savoir si le même client (le même nom de client ou le même numéro de client est peut-être déjà dans la liste), vous devez remplacer le paramètre equals()
. pour s'assurer qu'il vérifie si les champs pertinents (par exemple, les noms de clients) correspondent.
Remarque: N'oubliez pas de remplacer hashCode()
si vous souhaitez remplacer equals()
! Sinon, vous pourriez avoir des problèmes avec votre HashMaps et d'autres structures de données. Pour mieux expliquer pourquoi et les pièges à éviter, jetez un coup d'œil à Effective Java - de Josh Bloch chapitres sur equals()
et hashCode()
(le lien ne contient que des explications sur la raison pour laquelle vous devez implémenter hashCode()
lorsque vous implémentez equals()
, mais il existe une bonne couverture sur la façon de remplacer equals()
aussi).
Au fait, y a-t-il une restriction de commande sur votre poste? Si ce n’est pas le cas, un moyen légèrement plus simple de résoudre ce problème consiste à utiliser un Set<Customer>
comme suit:
Set<Customer> noDups = new HashSet<Customer>();
noDups.addAll(tmpListCustomer);
return new ArrayList<Customer>(noDups);
Ce qui éliminera les doublons pour vous, car les ensembles n'autorisent pas les doublons. Cependant, cela perdra tout ordre appliqué à tmpListCustomer
, puisque HashSet
n'a pas d'ordre explicite (vous pouvez éviter cela en utilisant un TreeSet
, mais ce n'est pas exactement lié à votre question). Cela peut simplifier un peu votre code.
Ajoutez simplement tous vos éléments à un Set
: il ne permet pas que ses éléments soient répétés. Si vous avez besoin d'une liste après, utilisez le nouveau constructeur ArrayList(theSet)
par la suite (où theSet
est l'ensemble résultant).
Je suppose que vous pourriez ne pas avoir Customer.equals()
correctement implémenté (ou pas du tout).
List.contains()
utilise equals()
pour vérifier si l'un de ses éléments est identique à l'objet transmis en tant que paramètre. Cependant, l'implémentation par défaut de equals
teste l'identité physique et non l'identité de valeur. Ainsi, si vous ne l'avez pas écrasé dans Customer
, il retournera la valeur false pour deux objets Client distincts ayant un état identique.
Voici les détails essentiels de comment implémenter equals
(et hashCode
, qui est sa paire - vous devez pratiquement toujours implémenter les deux si vous devez implémenter l'un ou l'autre). Comme vous ne nous avez pas montré la classe client, il est difficile de donner des conseils plus concrets.
Comme d'autres l'ont fait remarquer, il vaut mieux utiliser un ensemble plutôt que de faire le travail à la main, mais même pour cela, vous devez toujours implémenter ces méthodes.
La méthode "contient" recherche si la liste contient une entrée qui renvoie true à partir de Customer.equals (Object o). Si vous n'avez pas remplacé Equals (Object) dans Customer ou dans l'un de ses parents, il ne recherchera qu'une occurrence existante du même objet. C'est peut-être ce que vous vouliez, auquel cas votre code devrait fonctionner. Mais si vous recherchiez que deux objets ne représentent pas le même client, vous devez remplacer equals (Object) pour renvoyer true lorsque c'est le cas.
Il est également vrai que l’utilisation de l’une des implémentations de Set au lieu de List vous permettrait de supprimer les doublons automatiquement, et plus rapidement (pour toute autre tâche que les très petites listes). Vous aurez toujours besoin de fournir du code pour égaux.
Vous devez également remplacer hashCode () lorsque vous substituez equals ().
private void removeTheDuplicates(List<Customer>myList) {
for(ListIterator<Customer>iterator = myList.listIterator(); iterator.hasNext();) {
Customer customer = iterator.next();
if(Collections.frequency(myList, customer) > 1) {
iterator.remove();
}
}
System.out.println(myList.toString());
}
Presque toutes les réponses ci-dessus sont exactes, mais ce que je suggère, c’est d’utiliser une carte ou un ensemble lors de la création de la liste associée, et non après pour obtenir des performances. Parce que convertir une liste en un ensemble ou une carte, puis le reconvertir en liste est une tâche triviale.
Exemple de code:
Set<String> stringsSet = new LinkedHashSet<String>();//A Linked hash set
//prevents the adding order of the elements
for (String string: stringsList) {
stringsSet.add(string);
}
return new ArrayList<String>(stringsSet);
Deux suggestions:
Utilisez un HashSet au lieu d'une ArrayList. Cela accélèrera considérablement les contrôles contient () si vous avez une longue liste.
Assurez-vous que Customer.equals () et Customer.hashCode () sont correctement implémentés, c'est-à-dire qu'ils doivent être basés sur les valeurs combinées des champs sous-jacents de l'objet client.
IMHO meilleure façon comment le faire ces jours-ci:
Supposons que vous avez une collection " dups " et que vous souhaitez créer une autre collection contenant les mêmes éléments mais avec tous les doublons éliminés. Le one-liner suivant fait l'affaire.
Collection<collectionType> noDups = new HashSet<collectionType>(dups);
Cela fonctionne en créant un ensemble qui, par définition, ne peut pas contenir de doublons.
Basé sur Oracle doc.
Le moyen le plus propre est:
List<XXX> lstConsultada = dao.findByPropertyList(YYY);
List<XXX> lstFinal = new ArrayList<XXX>(new LinkedHashSet<GrupoOrigen>(XXX));
et remplacer hascode
et equals
par les propriétés de l'ID de chaque entité
Comme d'autres l'ont mentionné, vous n'implémentez probablement pas equals () correctement.
Cependant, vous devez également noter que ce code est considéré comme plutôt inefficace, car l'exécution peut correspondre au nombre d'éléments au carré.
Vous voudrez peut-être envisager d'utiliser une structure de jeu plutôt qu'une liste, ou de créer d'abord un jeu, puis de le transformer en liste.
La bonne réponse pour Java est d'utiliser un Set . Si vous avez déjà un List<Customer>
et que vous voulez le dupliquer
Set<Customer> s = new HashSet<Customer>(listCustomer);
Otherise utilise simplement une implémentation Set
HashSet
, TreeSet
directement et ignore la phase de construction List
.
Vous devrez remplacer hashCode()
ET equals()
sur les classes de votre domaine placées dans la variable Set
afin de vous assurer que le comportement que vous souhaitez réellement correspond à votre comportement. equals()
peut être aussi simple que comparer des identifiants uniques d'objets, ou aussi complexe que comparer chaque champ. hashCode()
peut être aussi simple que de renvoyer la hashCode()
de la représentation unique id 'String
ou la hashCode()
.
Utilisation de Java 8 stream api.
List<String> list = new ArrayList<>();
list.add("one");
list.add("one");
list.add("two");
System.out.println(list);
Collection<String> c = list.stream().collect(Collectors.toSet());
System.out.println(c);
Sortie:
Avant les valeurs: [un, un, deux]
Après les valeurs: [un, deux]