web-dev-qa-db-fra.com

Trouver tous les objets qui ont une propriété donnée dans une collection

J'ai un objet compliqué, tel qu'un chat, qui a de nombreuses propriétés, telles que l'âge, la nourriture préférée du chat, etc.

Un groupe de chats est stocké dans une collection Java, et je dois trouver tous les chats âgés de 3 ans ou ceux dont la nourriture préférée est Whiskas. Je peux certainement écrire une méthode personnalisée. qui trouve ces Chats avec une propriété spécifique, mais cela devient fastidieux avec de nombreuses propriétés, existe-t-il un moyen générique de le faire?

85
Jake

Vous pourriez écrire une méthode prenant une instance d'interface définissant une méthode check(Cat), cette méthode pouvant être implémentée avec la vérification de propriété souhaitée.

Mieux encore, rendez-le générique:

public interface Checker<T> {
    public boolean check(T obj);
}

public class CatChecker implements Checker<Cat> {
    public boolean check(Cat cat) {
        return (cat.age == 3); // or whatever, implement your comparison here
    }
}

// put this in some class
public static <T> Collection<T> findAll(Collection<T> coll, Checker<T> chk) {
    LinkedList<T> l = new LinkedList<T>();
    for (T obj : coll) {
         if (chk.check(obj))
             l.add(obj);
    }
    return l;
}

Bien sûr, comme d’autres personnes le disent, c’est pour cela que les bases de données relationnelles ont été créées ...

45
David Z

Essayez l’API de collections communes:

List<Cat> bigList = ....; // master list

Collection<Cat> smallList = CollectionUtils.select(bigList, new Predicate() {
    public boolean evaluate(Object o) {
        Cat c = (Cat)o;
        return c.getFavoriteFood().equals("Wiskas") 
            && c.getWhateverElse().equals(Something);
    }
});

Bien sûr, vous n’avez pas à utiliser de classe anonyme tous les fois, vous pouvez créer des implémentations de l’interface Predicate pour les recherches les plus utilisées.

62
Brian Dilley

J'utilise Google Collections (maintenant appelé Guava ) pour résoudre ce type de problème. Il existe une classe appelée Iterables qui peut prendre une interface appelée Predicate comme paramètre d'une méthode vraiment utile.

Cat theOne = Iterables.find(cats, new Predicate<Cat>() {
    public boolean apply(Cat arg) { return arg.age() == 3; }
});

Vérifiez-le ici !

55
rcouto

Avec Java 8 expression lambda, vous pouvez faire quelque chose comme:

cats.stream()
    .filter( c -> c.getAge() == 3 && c.getFavoriteFood() == WHISKAS )
    .collect(Collectors.toList());

Conceptuellement identique à l’approche Guava Predicate, mais elle semble beaucoup plus propre avec lambda

Ce n’est probablement pas une réponse valable pour OP mais il convient de le noter pour les personnes ayant des besoins similaires. :)

53
Adrian Shum

Je suggère d'utiliser Jxpath , cela vous permet de faire des requêtes sur les graphes d'objet comme si xpath ressemblait à

JXPathContext.newContext(cats).
     getValue("//*[@drinks='milk']")
11
flybywire

Encore une fois avec l'API de collections commune: Vous obtenez le code de type "vérificateur" lorsque vous implémentez le prédicat séparément: -

public class CatPredicate implements Predicate {

    private int age; 


    public CatPredicate(int age) {
        super();
        this.age = age;
    }


    @Override
    public boolean evaluate(Object o) {
        Cat c (Cat)o;
        return c.getAge()==this.age;
    }

}

qui s'utilise comme: -

CollectionUtils.filter(catCollectionToFilter, new CatPredicate(3))
5
user151888

Vous pouvez utiliser lambdaj . Des choses comme celles-ci sont triviales, la syntaxe est vraiment fluide:

Person me = new Person("Mario", "Fusco", 35);
Person luca = new Person("Luca", "Marrocco", 29);
Person biagio = new Person("Biagio", "Beatrice", 39);
Person celestino = new Person("Celestino", "Bellone", 29);
List<Person> meAndMyFriends = asList(me, luca, biagio, celestino);
List<Person> oldFriends = filter(having(on(Person.class).getAge(), greaterThan(30)), meAndMyFriends);

et vous pouvez faire des choses beaucoup plus compliquées. Il utilise hamcrest pour les Matchers. Certains diront que ce n'est pas du style Java, mais il est amusant de voir comment ce type a tordu Java pour faire un peu de programmation fonctionnelle. Regardez le code source aussi, c'est plutôt de la science-fiction .

4
Gismo Ranas

Juste pour votre information, il y a 3 autres réponses à cette question qui utilisent Guava, mais aucune ne répond à la question. Le demandeur a indiqué qu'il souhaitait trouver tous les chats possédant une propriété correspondante, par exemple. âge de 3 ans. Iterables.find ne correspond qu’à un, s’il en existe. Vous auriez besoin d'utiliser Iterables.filter pour y parvenir si vous utilisez de la goyave, par exemple:

Iterable<Cat> matches = Iterables.filter(cats, new Predicate<Cat>() {
    @Override
    public boolean apply(Cat input) {
        return input.getAge() == 3;
    }
});
3
Breeno

Utilisation des collections communes:

EqualPredicate nameEqlPredicate = new EqualPredicate(3);
BeanPredicate beanPredicate = new BeanPredicate("age", nameEqlPredicate);
return CollectionUtils.filter(cats, beanPredicate);
3

Les solutions basées sur des prédicats, des filtres et des itérateurs/comparateurs personnalisés sont bien, mais elles ne fournissent pas d'analogue avec index de base de données. Par exemple: je voudrais rechercher dans la collection de chats de différentes manières: par sexe et par âge et par âge, ainsi cela ressemble à deux index: 1) [sexe, âge] 2) [âge]. Les valeurs, accessibles par ces index, pourraient être hachées pour implémenter une recherche rapide, sans itérer la collection entière. Y a-t-il une telle solution?

1

Utilisez Google Guava.

final int lastSeq = myCollections.size();
Clazz target = Iterables.find(myCollections, new Predicate<Clazz>() {
    @Override
    public boolean apply(@Nullable Clazz input) {
      return input.getSeq() == lastSeq;
    }
});

Je pense utiliser cette méthode.

1
Honeymon

Cela ressemble beaucoup à quelque chose pour lequel vous utiliseriez LINQ dans .NET

Bien qu'il n'y ait pas encore de "réelle" implémentation LINQ pour Java, vous voudrez peut-être jeter un coup d'œil à Quaere qui pourrait faire ce que vous décrivez assez facilement.

1
Eric Petroelje

Vous pouvez utiliser quelque chose comme JoSQL et écrire 'SQL' sur vos collections: http://josql.sourceforge.net/

Cela ressemble à ce que vous voulez, avec l'avantage supplémentaire de pouvoir effectuer des requêtes plus complexes.

1
duwanis

Vous pouvez essayer une partie du code générique du projet Apache Commons. Le sous-projet Collections fournit du code permettant de rechercher des objets correspondant à un prédicat donné, ainsi qu'un grand nombre de prédicats (égaux, nuls, instance de, etc.). Le sous-projet BeanUtils vous permet de créer des prédicats qui testent les propriétés des haricots.

Utilisez la classe CollectionUtils pour effectuer une recherche dans une collection. Il existe quelques méthodes pour cela, mais consultez la méthode select (), en particulier. Utilisez les classes suivantes pour construire des prédicats ou écrivez les vôtres: Predicate , PredicateUtils , BeanPredicate .

Tout cela est parfois un peu lourd, mais au moins c'est générique! :-)

1
Rich Dougherty

JFilter http://code.google.com/p/jfilter/ correspond à vos besoins.

JFilter est une bibliothèque open source simple et hautes performances pour interroger la collection de beans Java.

Principales caractéristiques

  • Prise en charge des propriétés de collection (Java.util.Collection, Java.util.Map et Array).
  • Support de collection dans la collection de n'importe quelle profondeur.
  • Prise en charge des requêtes internes.
  • Prise en charge des requêtes paramétrées.
  • Peut filtrer 1 million d’enregistrements en quelques 100 ms.
  • Filtre (requête) est donné au format JSON simple, c'est comme les requêtes Mangodb. Voici quelques exemples.
    • {"id": {"$ le": "10"}
      • où la propriété object id est inférieure à égale à 10.
    • {"id": {"$ in": ["0", "100"]}}
      • où la propriété object id est 0 ou 100.
    • {"lineItems": {"lineAmount": "1"}}
      • où lineItems propriété de collection de type paramétré a lineAmount égal à 1.
    • {"$ et": [{"id": "0"}, {"billingAddress": {"ville": "DEL"}}}}.
      • où la propriété id est 0 et la propriété billingAddress.city est DEL.
    • {"lineItems": {"taxes": {"clé": {"code": "TPS"}, "valeur": {"$ gt": "1.01"}}}}
      • où la propriété de collection lineItems de type paramétré ayant la taxe type de carte, la propriété de type paramétré a un code égal à la valeur GST supérieure à 1.01.
    • {'$ ou': [{'code': '10'}, {'skus': {'$ et': [{'price': {'$ in': ['20', '40']} }, {'code': 'RedApple'}]}}]}
      • Sélectionnez tous les produits dont le code de produit est 10 ou le prix de référence en 20 et 40 et le code de référence est "RedApple".
0
Kamran Ali Khan

vous pouvez rechercher un élément dans une liste comme suit. bonne chance!

int _searchList(List<T> list,T item) {
int idx = -1;
idx = Collections.binarySearch(list,item,
        new Comparator<T>() {
            public int compare(Titm1,
                    Titm2) {

                // key1
                if (itm1.key1.compareTo(itm2.key1) != 0) {
                    return itm1.key2.compareTo(itm2.key2);
                }

                // key2
                return itm1.key2
                        .compareTo(itm2.key2);
            }
        });

return idx;

}

0
han

Vous pouvez stocker ces objets sur une base de données. Si vous ne souhaitez pas utiliser un serveur de base de données complet, vous pouvez utiliser un serveur intégré, tel que HSQLDB. Vous pouvez ensuite utiliser Hibernate ou BeanKeeper (plus simple à utiliser) ou un autre ORM pour mapper des objets sur des tables. Vous continuez à utiliser le modèle OO) et obtenez le stockage avancé et les avantages de requêtes d'une base de données.

0
Cesar

Guava dispose de capacités de recherche très puissantes pour ce type de problèmes. Par exemple, si votre région recherche un objet en fonction de l'une de ses propriétés, vous pouvez envisager:

Iterables.tryFind(listOfCats, new Predicate<Cat>(){
    @Override
    boolean apply(@Nullable Cat input) {
        return "tom".equalsIgnoreCase(input.name());
    }
}).or(new Cat("Tom"));

s'il est possible que le chat Tom ne figure pas dans la liste, il sera retourné, vous permettant ainsi d'éviter NPE.

0
Ivan Hristov

Voici une idée de classe de chercheur que vous pouvez paramétrer avec les valeurs spécifiques que vous souhaitez rechercher.

Vous pouvez aussi aller plus loin et stocker les noms des propriétés, probablement dans une carte avec les valeurs souhaitées. Dans ce cas, vous utiliseriez la réflexion sur la classe Cat pour appeler les méthodes appropriées en fonction des noms de propriété.

public class CatSearcher {

  private Integer ageToFind = null;
  private String  foodToFind = null;

  public CatSearcher( Integer age, String food ) {
    this.ageToFind = age;
    this.foodToFind = food;
  }

  private boolean isMatch(Cat cat) {
    if ( this.ageToFind != null && !cat.getAge().equals(this.ageToFind) ) {
       return false;
    }
    if ( this.foodToFind != null && !cat.getFood().equals(this.foodToFind) {
       return false;
    }
    return true;
  }

  public Collection<Cat> search( Collection<Cat> listToSearch ) {
    // details left to the imagination, but basically iterate over
    // the input list, call isMatch on each element, and if true
    // add it to a local collection which is returned at the end.
  }

}
0
Dave Costa

Un problème très courant et j'ai utilisé des collections Google et voici mon code

public class FindByIdPredicate implements Predicate<IDObject> {

private Long entityId;

public FindByIdPredicate(final Long entityId) {
    this.entityId = entityId;

}

@Override
public boolean apply(final IDObject input) {
    return input.getId().equals(this.entityId);
}

/**
     * Pass in the Collection
 * @param Collection
 * @return IdObject if present or null
 */
public IDObject getEntity(final Collection<? extends IDObject> collection) {

    for (IDObject idObject : collection) {
        if (this.apply(idObject)) {
            return idObject;
        }
    }
    return null;

}

/**
 * @param Set
 * @return IdObject if present or null
 */
@SuppressWarnings("unchecked")
public <T> T getEntity(final Set<? extends IDObject> set) {

    for (IDObject idObject : set) {
        if (this.apply(idObject)) {
            return (T) idObject;
        }
    }
    return null;

}

}

J'espère que cela t'aides

0
vsingh