290 votes

Java List.contains(Objet dont la valeur du champ est égale à x)

Je veux vérifier si un List contient un objet qui a un champ avec une certaine valeur. Je pourrais utiliser une boucle pour vérifier, mais j'étais curieux de savoir s'il y avait quelque chose de plus 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 démontrer grossièrement ce que j'essaie d'obtenir.

Aussi, juste pour clarifier, la raison pour laquelle je ne veux pas utiliser une simple boucle est que ce code va actuellement aller dans une boucle qui est dans une boucle qui est dans une boucle. Pour des raisons de lisibilité, je ne veux pas continuer à ajouter des boucles à ces boucles. Je me suis donc demandé s'il existait des alternatives simples (ou presque).

393voto

Josh M Points 4212

Cours d'eau

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

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

Ou alternativement, vous pourriez essayer quelque chose comme ça :

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

Cette méthode renverra true si le List<MyObject> contient un MyObject avec le nom name . Si vous voulez effectuer une opération sur chacune des MyObject que getName().equals(name) alors vous pourriez essayer quelque chose comme ça :

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

o représente un MyObject instance.

Alternativement, comme les commentaires le suggèrent (Merci MK10), vous pourriez utiliser la fonction Stream#anyMatch méthode :

public boolean containsName(final List<MyObject> list, final String name){
    return list.stream().anyMatch(o -> o.getName().equals(name));
}

85voto

TJamesBoone Points 2509

Vous avez deux choix.

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

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

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

Maintenant, disons que vous ne vous souciez que du nom du MyObject, qu'il doit être unique et que si deux `MyObjects` ont le même nom, ils doivent être considérés comme égaux. Dans ce cas, vous voudriez surcharger 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 MyObject avec le nom "foo" en faisant comme suit :

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

Toutefois, cette option peut ne pas vous convenir si.. :

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

Si l'un ou l'autre de ces cas se présente, vous choisirez l'option 2 :

2. Écrivez votre propre méthode utilitaire :

public static boolean containsLocation(Collection<MyObject> c, String 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) et y ajouter votre propre méthode :

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

Malheureusement, il n'y a pas de meilleur moyen de contourner ce problème.

49voto

GabrielBB Points 361

Voici comment procéder en utilisant Java 8+ :

boolean isJohnAlive = list.stream().anyMatch(o -> "John".equals(o.getName());

26voto

Brian Agnew Points 143181

Google Guava

Si vous utilisez Goyave vous pouvez adopter une approche fonctionnelle et procéder comme suit

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

ce qui semble un peu verbeux. Cependant, le prédicat est un objet 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 surcharger equals() pour un comportement particulier.

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

22voto

Aaron Digulla Points 143830

Collection.contains() est mis en œuvre en appelant equals() sur chaque objet jusqu'à ce que l'un d'entre eux renvoie true .

Une façon d'implémenter ceci est donc de remplacer equals() mais bien sûr, on ne peut avoir qu'un seul égal.

Des cadres comme Goyave Utilisez 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 intègrent cette fonction. Sur Groovy par exemple, vous écrivez simplement :

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

Java 8 nous a également facilité la vie :

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

Si vous vous intéressez à ce genre de choses, je vous conseille le livre "Beyond Java". Il contient de nombreux exemples des nombreuses lacunes de Java et de la façon dont d'autres langages font mieux.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X