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?
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 ...
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.
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 !
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. :)
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']")
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))
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 .
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;
}
});
Utilisation des collections communes:
EqualPredicate nameEqlPredicate = new EqualPredicate(3);
BeanPredicate beanPredicate = new BeanPredicate("age", nameEqlPredicate);
return CollectionUtils.filter(cats, beanPredicate);
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?
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.
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.
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.
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! :-)
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
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;
}
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.
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.
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.
}
}
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