web-dev-qa-db-fra.com

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

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

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 approximativement ce que j'essaie de réaliser.

Aussi, juste pour clarifier, la raison pour laquelle je ne veux pas utiliser une boucle simple est parce que ce code va actuellement 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. Alors je me suis demandé s'il y avait des alternatives simples (ish).

137
Rudi Kershaw

Ruisseaux

Si vous utilisez Java 8, vous pourriez 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 bien, vous pouvez essayer quelque chose comme ceci:

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 une MyObject avec le nom name. Si vous souhaitez effectuer une opération sur chacune des MyObjects que getName().equals(name), alors vous pouvez essayer quelque chose comme ceci:

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

o représente une instance MyObject.

169
Josh M

Vous avez deux choix.

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

Disons, par exemple, que vous avez cette classe d'objet:

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

Maintenant, disons que vous ne vous souciez que du nom de MyObject, qu’il doit être unique, donc si deux `MyObject` ont le même nom, ils doivent être considérés comme égaux. Dans ce cas, vous voudriez remplacer la méthode `equals ()` (ainsi que la méthode `hashcode ()`) afin qu'elle compare les noms afin de 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" de la manière suivante:

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 voulez uniquement vérifier si une collection contient des `MyObject`s 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 changer.

Si l'un ou l'autre est le cas, vous voudrez l'option 2:

2. Écrivez votre propre méthode d’utilité:

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 meilleure solution.

73
James Dunn

Google Guava

Si vous utilisez Guava , 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();

qui a l'air un peu prolixe. 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. Il n'est pas nécessaire de remplacer equals() pour un comportement particulier.

Comme indiqué ci-dessous, la structure Java.util.Stream intégrée à Java 8 et versions ultérieures fournit quelque chose de similaire.

24
Brian Agnew

Collection.contains() est implémenté en appelant equals() sur chaque objet jusqu'à ce que l'on retourne true.

Donc, une façon d'implémenter ceci est de remplacer equals() mais bien sûr, vous ne pouvez avoir qu'un égal.

Les cadres 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.

Ceci est intégré dans les autres langues construites au-dessus de VM. Dans Groovy , par exemple, vous écrivez simplement:

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

Java 8 nous a également rendu la vie plus facile:

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

Si cela vous intéresse, je vous suggère le livre "Au-delà de Java". Il contient de nombreux exemples des nombreuses faiblesses de Java et de l'amélioration des autres langages.

18
Aaron Digulla

Recherche binaire

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

Collections.binarySearch(list, new YourObject("a1", "b",
                "c"), new Comparator<YourObject>() {

            @Override
            public int compare(YourObject o1, YourObject o2) {
                return o1.getName().compareTo(o2.getName());
            }
        });

qui retournera un nombre négatif si l'objet n'est pas présent dans la collection ou bien il retournera la index de l'objet. Avec cela, vous pouvez rechercher des objets avec différentes stratégies de recherche.

17
Debojit Saikia

Carte

Vous pouvez créer un Hashmap<String, Object> en utilisant l'une des valeurs comme clé, puis voir si yourHashMap.keySet().contains(yourValue) renvoie true.

7
user7110558

Voici comment faire avec Java 8+:

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

Collections Eclipse

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

ListIterable<MyObject> list = ...;

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

Si vous effectuez fréquemment des opérations de ce type, il est préférable d'extraire une méthode qui indique si le type a l'attribut.

public class MyObject
{
    private final String name;

    public MyObject(String name)
    {
        this.name = name;
    }

    public boolean named(String name)
    {
        return Objects.equals(this.name, name);
    }
}

Vous pouvez utiliser la forme alternative anySatisfyWith() avec une référence à une méthode.

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

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

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

Remarque: je suis un partisan des ollections Eclipse.

6
Craig P. Motlin

Predicate

Si vous n'utilisez pas Java 8, ou une bibliothèque qui vous donne 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<T>{
        boolean contains(T item);
    }

    static class CollectionUtil{

        public static <T> T find(final Collection<T> collection,final  Predicate<T> 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 l'interface de prédicat, et je la passe à la classe util.

Quel est l'avantage de le faire à ma manière? vous avez une méthode qui consiste à chercher dans n'importe quelle collection de types. et vous n'avez pas à créer des méthodes séparées si vous souhaitez effectuer une recherche par champ différent. tout ce que vous devez faire est de fournir un prédicat différent qui peut être détruit dès qu’il n’est plus utile /

si vous voulez l'utiliser, il vous suffit d'appeler la méthode et de définir votre prédicat

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

La méthode contains utilise equals en interne. Vous devez donc redéfinir la méthode equals pour votre classe selon vos besoins.

Btw, cela ne semble pas correct du point de vue de la synthèse: 

new Object().setName("John")
2
Juned Ahsan

Voici une solution utilisant Guava

private boolean checkUserListContainName(List<User> userList, final String targetName){

    return FluentIterable.from(userList).anyMatch(new Predicate<User>() {
        @Override
        public boolean apply(@Nullable User input) {
            return input.getName().equals(targetName);
        }
    });
}
2
Linh

Si vous devez exécuter cette List.contains(Object with field value equal to x) à plusieurs reprises, une solution simple et efficace serait la suivante:

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

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

1
Magda

Malgré Java 8 SDK, de nombreuses bibliothèques d'outils de collection peuvent vous aider à travailler, 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 );
0
Pasha Gharibi