Java List.contains (object avec une valeur de champ égale à x)

Je veux vérifier si une List contient un object qui a un champ avec une certaine valeur. Maintenant, je pourrais utiliser une boucle pour vérifier et vérifier, mais j’étais curieux de savoir s’il y avait plus de code efficace.

Quelque chose comme;

 if(list.contains(new Object().setName("John"))){ //Do some stuff } 

Je sais que le code ci-dessus ne fait rien, c’est juste pour montrer en gros ce que j’essaie de réaliser.

De plus, juste pour clarifier, la raison pour laquelle je ne veux pas utiliser une boucle simple est que ce code ira dans une boucle qui se trouve dans une boucle qui se trouve dans une boucle. Pour plus de lisibilité, je ne veux pas continuer à append des boucles à ces boucles. Je me suis donc demandé s’il existait des alternatives simples (ish).

Ruisseaux

Si vous utilisez Java 8, vous pourriez peut-être essayer quelque chose comme ceci:

 public boolean containsName(final List list, final Ssortingng name){ return list.stream().filter(o -> o.getName().equals(name)).findFirst().isPresent(); } 

Ou bien, vous pourriez essayer quelque chose comme ceci:

 public boolean containsName(final List list, final Ssortingng name){ return list.stream().map(MyObject::getName).filter(name::equals).findFirst().isPresent(); } 

Cette méthode retournera true si List contient un object MyObject portant le nom name . Si vous souhaitez effectuer une opération sur chacun des MyObject que getName().equals(name) , vous pouvez essayer quelque chose comme ceci:

 public void perform(final List list, final Ssortingng name){ return list.stream().filter(o -> o.getName().equals(name)).forEach( o -> { //... } ); } 

o représente une occurrence de MyObject .

Vous avez deux choix.

1. Le premier choix, qui est préférable, est de remplacer la méthode `equals ()` dans votre classe Object.

Disons, par exemple, que vous avez cette classe Object:

 public class MyObject { private Ssortingng name; private Ssortingng location; //getters and setters } 

Maintenant, disons que vous ne vous souciez que du nom de MyObject, qu’il devrait être unique, donc si deux `MyObject`s ont le même nom, ils devraient être considérés comme égaux. Dans ce cas, vous voudrez remplacer la méthode `equals ()` (et aussi la méthode `hashcode ()`) afin qu’elle compare les noms pour déterminer l’égalité.

Une fois que vous avez fait cela, vous pouvez vérifier si une collection contient un object MyObject nommé “foo” par exemple:

 MyObject object = new MyObject(); object.setName("foo"); collection.contains(object); 

Cependant, cela pourrait ne pas être une option pour vous si:

  • Vous utilisez à la fois le nom et l’emplacement pour vérifier l’égalité, mais vous souhaitez uniquement vérifier si une collection a des objects `MyObject` avec un certain emplacement. Dans ce cas, vous avez déjà remplacé `equals ()`.
  • `MyObject` fait partie d’une API que vous n’avez pas la liberté de modifier.

Si tel est le cas, vous voudrez l’option 2:

2. Écrivez votre propre méthode utilitaire:

 public static boolean containsLocation(Collection c, Ssortingng location) { for(MyObject o : c) { if(o != null && o.getLocation.equals(location)) { return true; } } return false; } 

Vous pouvez également étendre ArrayList (ou une autre collection), puis y append votre propre méthode:

 public boolean containsLocation(Ssortingng location) { for(MyObject o : this) { if(o != null && o.getLocation.equals(location)) { return true; } } return false; } 

Malheureusement, il n’y a pas de meilleure solution.

Google Goyave

Si vous utilisez Guava , vous pouvez adopter une approche fonctionnelle et procéder comme suit:

 FluentIterable.from(list).find(new Predicate() { public boolean apply(MyObject input) { return "John".equals(input.getName()); } }).Any(); 

qui a l’air un peu verbeux. Cependant, le prédicat est un object et vous pouvez fournir différentes variantes pour différentes recherches. Notez comment la bibliothèque elle-même sépare l’itération de la collection et la fonction que vous souhaitez appliquer. Vous n’avez pas besoin de remplacer equals() pour un comportement particulier.

Comme indiqué ci-dessous, le framework java.util.Stream intégré à Java 8 et aux versions ultérieures fournit quelque chose de similaire.

Recherche binary

Vous pouvez utiliser Collections.binarySearch pour rechercher un élément dans votre liste (en supposant que la liste soit sortingée):

 Collections.binarySearch(list, new YourObject("a1", "b", "c"), new Comparator() { @Override public int compare(YourObject o1, YourObject o2) { return o1.getName().compareTo(o2.getName()); } }); 

qui renverra un nombre négatif si l’object n’est pas présent dans la collection ou s’il retournera l’ index de l’object. Avec cela, vous pouvez rechercher des objects avec différentes stratégies de recherche.

Collection.contains() est implémentée en appelant equals() sur chaque object jusqu’à ce que l’un renvoie true .

Une des façons d’implémenter cela est de remplacer les equals() mais bien sûr, vous ne pouvez en avoir qu’un seul.

Les frameworks tels que Guava utilisent donc des prédicats pour cela. Avec Iterables.find(list, predicate) , vous pouvez rechercher des champs arbitraires en plaçant le test dans le prédicat.

D’autres langages construits au-dessus de la VM ont cette fonctionnalité intégrée. Dans Groovy , par exemple, vous écrivez simplement:

 def result = list.find{ it.name == 'John' } 

Java 8 nous a également facilité la vie:

 List result = list.stream() .filter(it -> "John".equals(it.getName()) .collect(Collectors.toList()); 

Si vous vous souciez de choses comme ça, je suggère le livre “Beyond Java”. Il contient de nombreux exemples des nombreuses lacunes de Java et de la manière dont les autres langages fonctionnent mieux.

Collections Eclipse

Si vous utilisez les collections Eclipse , vous pouvez utiliser la méthode anySatisfy() . ListAdapter votre List dans un ListAdapter ou modifiez votre List en une ListIterable si possible.

 ListIterable list = ...; boolean result = list.anySatisfy(myObject -> myObject.getName().equals("John")); 

Si vous faites fréquemment des opérations comme celle-ci, il est préférable d’extraire une méthode qui répond à la question de savoir si le type possède l’atsortingbut.

 public class MyObject { private final Ssortingng name; public MyObject(Ssortingng name) { this.name = name; } public boolean named(Ssortingng name) { return Objects.equals(this.name, name); } } 

Vous pouvez utiliser le formulaire alternatif anySatisfyWith() avec une référence de méthode.

 boolean result = list.anySatisfyWith(MyObject::named, "John"); 

Si vous ne pouvez pas changer votre List en une ListIterable , voici comment utiliser ListAdapter .

 boolean result = ListAdapter.adapt(list).anySatisfyWith(MyObject::named, "John"); 

Note: Je suis un committer pour les collections Eclipse.

Carte

Vous pouvez créer un Hashmap utilisant l’une des valeurs comme clé, puis voir si yourHashMap.keySet().contains(yourValue) renvoie true.

Predicate

Si vous n’utilisez pas Java 8 ou une bibliothèque qui vous offre plus de fonctionnalités pour gérer les collections, vous pouvez implémenter quelque chose qui peut être plus réutilisable que votre solution.

 interface Predicate{ boolean contains(T item); } static class CollectionUtil{ public static  T find(final Collection collection,final Predicate predicate){ for (T item : collection){ if (predicate.contains(item)){ return item; } } return null; } // and many more methods to deal with collection } 

J’utilise quelque chose comme ça, j’ai une interface de prédicat, et je passe son implémentation à ma classe util.

Quel est l’avantage de le faire à ma façon? vous avez une méthode qui traite de la recherche dans n’importe quelle collection de types. et vous n’avez pas besoin de créer des méthodes distinctes si vous souhaitez effectuer une recherche par champ différent. alll ce que vous devez faire est de fournir des prédicats différents qui peuvent être détruits dès qu’ils ne sont plus utiles /

si vous voulez l’utiliser, tout ce que vous devez faire est d’appeler la méthode et de définir votre prédicat

 CollectionUtil.find(list, new Predicate{ public boolean contains(T item){ return "John".equals(item.getName()); } }); 

contains méthode utilise des equals interne. Donc, vous devez remplacer la méthode equals pour votre classe selon vos besoins.

Btw cela ne semble pas correct du sharepoint vue statistique:

 new Object().setName("John") 

Si vous devez effectuer cette List.contains(Object with field value equal to x) plusieurs resockets, une solution simple et efficace serait:

 List fieldOfInterestValues = new ArrayList; for(Object obj : List) { fieldOfInterestValues.add(obj.getFieldOfInterest()); } 

Ensuite, le List.contains(Object with field value equal to x) aurait le même résultat que fieldOfInterestValues.contains(x);

Malgré le JAVA 8 SDK, il existe de nombreux outils de collecte avec lesquels les bibliothèques peuvent vous aider, par exemple: http://commons.apache.org/proper/commons-collections/

 Predicate condition = new Predicate() { boolean evaluate(Object obj) { return ((Sample)obj).myField.equals("myVal"); } }; List result = CollectionUtils.select( list, condition ); 

Voici une solution utilisant Guava

 private boolean checkUserListContainName(List userList, final Ssortingng targetName){ return FluentIterable.from(userList).anyMatch(new Predicate() { @Override public boolean apply(@Nullable User input) { return input.getName().equals(targetName); } }); }