1
votes

Comment filtrer une liste d'objets Java à l'exécution où la variable sur laquelle je filtre n'est pas connue à l'avance?

J'ai une liste d'objets java, et je souhaite utiliser stream pour les filtrer lors de l'exécution. Cependant, la variable sur laquelle je veux filtrer n'est connue qu'au moment de l'exécution.

Par exemple, l'utilisateur dit que je veux une liste de tous les chats dont la longueur de la fourrure est supérieure à 3 cm, je devrais pouvoir le faire

XXX

Le getter getFurLength () doit cependant être appelé dynamiquement - si l'utilisateur souhaite plutôt filtrer par couleur des yeux, je devrais pouvoir appeler

{
  eyeColour:{
    operator: "equal_to",
    value: "BLUE"
  },
  furLength: {
    operator: "greater_than",
    value: 3
  }
}

Comment y parvenir sans écrire au préalable tous les filtres possibles?

Idéalement, l'utilisateur devrait envoyer quelque chose comme:

cats.stream().filter(cat -> cat.getEyeColour() == Colour.BLUE).collect(Collectors.toList());

et le code devrait être capable de générer les filtres dynamiquement en fonction de ces critères.


2 commentaires

Vous devez avoir un mappage quelque part qui évalue quelle entrée utilisateur peut aboutir à quel Predicate et généraliser l'instruction pour utiliser cats.stream (). Filter (predicateAsInput) .collect (Collectors.to‌ Liste ());


Désérialisez le json dans une classe contenant le champ, l'opérateur et la valeur des trois éléments. Générez un Predicate à partir de cela en extrayant la valeur réelle à l'aide de la réflexion pour créer une Function , puis appliquez l'opérateur pour voir si la valeur correspond.


3 Réponses :


0
votes

Rendez-le réutilisable avec une fonction.

filterCats(cats, cat -> cat.getFurLength() > 3)

Et puis utilisez-le avec:

filterCats(cats, cat -> cat.getEyeColour() == Colour.BLUE)

Ou,

List<Cat> filterCats(cats, Predicate<Cat> filter) {
    return cats.stream().filter(filter).collect(Collectors.toList());
}


2 commentaires

La question est de savoir comment créer cat -> cat.getEyeColour () == Colour.BLUE à partir de données utilisateur comme JSON. Pour citer OP " Comment y parvenir sans écrire au préalable tous les filtres possibles ?"


Beaucoup de déclarations if? Ne pensez pas qu'il existe un raccourci prêt à l'emploi pour quelque chose comme celui-ci, comme TOP le recherche probablement.



1
votes

En supposant que votre classe Cat respecte la convention JavaBean, vous pouvez utiliser java.beans.PropertyDescriptor pour accéder à la Méthode getter en fonction du nom de la propriété.

Cela nous permet de savoir à quel type de valeur nous avons affaire. S'il est numérique, nous pouvons gérer supérieur_à et d'autres opérateurs, mais s'il n'est pas numérique, nous ne devons gérer que l'opérateur equals_to .

Une solution "simplifiée" et très limitée pourrait ressembler à:

REMARQUE:
- La solution ne prend pas en charge les types numériques primitifs tels que int . Utilisez à la place Integer , Double etc.
- Je convertis tous les nombres en BigDecimal et j'utilise compareTo pour simplifier la comparaison de types numériques, si vous rencontrez des bogues pour de grands nombres ou des nombres très précis, n'hésitez pas à les remplacer par des comparaison de types).
- pour le contrôle d'égalité, il compare la représentation sous forme de chaîne d'objets (résultat de toString () ), donc pour Color vous ne pouvez pas utiliser BLUE mais votre JSON devrait contenir java.awt.Color [r = 0, g = 0, b = 255] )

Cat{eyeColor=java.awt.Color[r=0,g=0,b=255], furLength=4}
Cat{eyeColor=java.awt.Color[r=0,g=0,b=255], furLength=5}

qui peut être utilisé comme:

XXX

class CatsDemo {
    public static void main(String[] args) {

        String json = 
                "{\n" +
                "  eyeColour:{\n" +
                "    operator: \"equal_to\",\n" +
                "    value: \"java.awt.Color[r=0,g=0,b=255]\"\n" +
                "  },\n" +
                "  furLength: {\n" +
                "    operator: \"greater_than\",\n" +
                "    value: \"3\"\n" +
                "  }\n" +
                "}";

        List<Cat> cats = List.of(
                new Cat(Color.blue, 1),
                new Cat(Color.blue, 2),
                new Cat(Color.blue, 3),
                new Cat(Color.blue, 4),
                new Cat(Color.blue, 5),
                new Cat(Color.yellow, 1),
                new Cat(Color.yellow, 2),
                new Cat(Color.yellow, 3),
                new Cat(Color.yellow, 4),
                new Cat(Color.yellow, 5)
        );

        cats.stream()
            .filter(PredicatesUtil.filters(Cat.class, json))
            .forEach(System.out::println);
    }
}

Sortie:

class Cat {
    private Color eyeColour;
    private Integer furLength;

    Cat(Color eyeColor, Integer furLength) {
        this.eyeColour = eyeColor;
        this.furLength = furLength;
    }

    public Color getEyeColour() {
        return eyeColour;
    }

    public Integer getFurLength() {
        return furLength;
    }

    public void setEyeColour(Color eyeColour) {
        this.eyeColour = eyeColour;
    }

    public void setFurLength(Integer furLength) {
        this.furLength = furLength;
    }

    @Override
    public String toString() {
        return "Cat{" +
                "eyeColor=" + eyeColour +
                ", furLength=" + furLength +
                '}';
    }
}


0 commentaires

0
votes

Pour ce que ça vaut: la bibliothèque Apache Commons BeanUtils est spécialisée dans l'accès aux bean propriétés de manière dynamique.

Voir BeanPropertyValueEqualsPredicate pour une compréhension. Ceci n'est qu'une solution pour les correspondances d'égalité.


0 commentaires