如何在运行时过滤java对象列表,而我提前知道过滤的变量?

问题描述 投票:1回答:3

我有一个java对象列表,我想使用流在运行时过滤它们。但是,我想要过滤的变量只在运行时才知道。

例如,用户说我想要一张毛皮长度超过3厘米的所有猫的清单,我应该可以做到

cats.stream().filter(cat -> cat.getFurLength() > 3).collect(Collectors.toList());

然而,应该动态调用getFurLength()getter - 如果用户想要通过眼睛颜色进行过滤,那么我应该可以调用

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

如何在不事先编写所有可能的过滤器的情况下实现此目的?

理想情况下,用户应该发送以下内容:

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

并且代码应该能够根据这些标准动态生成过滤器。

java java-8 java-stream predicate
3个回答
1
投票

假设你的Cat类遵循JavaBean约定,你可以使用java.beans.PropertyDescriptor根据属性名访问getter Method

这使我们能够了解我们正在处理的价值类型。如果它是数字,我们可以处理greater_than和其他运算符,但如果它是非数字的,我们应该只处理equals_to运算符。

“简化”和非常有限的解决方案可能如下所示:

注意: - 解决方案不支持像int这样的原始数字类型。请改用IntegerDouble等。 - 我正在将所有数字转换为BigDecimal并使用compareTo来简化数字类型比较,如果你得到任何大数字或非常精确的错误,可以随意替换它与正确的类型比较)。 - 对于等式检查,它比较对象的字符串表示(toString()的结果),因此对于Color,你不能使用BLUE,但你的JSON需要持有java.awt.Color[r=0,g=0,b=255]

class PredicatesUtil {

    static <T> Predicate<T> filters(Class<T> clazz, String filtersJson) {

        JSONObject jsonObject = new JSONObject(filtersJson);

        List<Predicate<T>> predicateList = new ArrayList<>();
        for (String property : jsonObject.keySet()) {
            JSONObject filterSettings = jsonObject.getJSONObject(property);

            try {
                String operator = filterSettings.getString("operator");
                String value = filterSettings.getString("value");
                predicateList.add(propertyPredicate(clazz, property, operator, value));
            } catch (IntrospectionException e) {
                throw new RuntimeException(e);
            }
        }
        return combinePredicatesUsingAND(predicateList);
    }

    static <T> Predicate<T> combinePredicatesUsingAND(List<Predicate<T>> predicateList) {
        return t -> {
            for (Predicate<T> pr : predicateList) {
                if (!pr.test(t))
                    return false;
            }
            return true;
        };
    }

    static <T> Predicate<T> propertyPredicate(Class<T> clazz, String property,
                                              String operator, String value)
            throws IntrospectionException {

        final Method m = new PropertyDescriptor(property, clazz).getReadMethod();
        final Class<?> returnType = m.getReturnType();

        return obj -> {
            try {
                Object getterValue = m.invoke(obj);
                if (Number.class.isAssignableFrom(returnType)) {
                    BigDecimal getValue = new BigDecimal(getterValue.toString());
                    BigDecimal numValue = new BigDecimal(value);

                    int compared = getValue.compareTo(numValue);
                    if (operator.equalsIgnoreCase("equal_to")) {
                        return compared == 0;
                    } else if (operator.equalsIgnoreCase("lesser_than")) {
                        return compared < 0;
                    } else if (operator.equalsIgnoreCase("greater_than")) {
                        return compared > 0;
                    } else {
                        throw new RuntimeException("not recognized operator for numeric type: " + operator);
                    }
                } else {
                    //System.out.println("testing non-numeric, only euals_to");
                    if (operator.equalsIgnoreCase("equal_to")) {
                        return value.equalsIgnoreCase(getterValue.toString());
                    }
                    throw new RuntimeException("not recognized operator: " + operator);
                }
            } catch (IllegalAccessException | InvocationTargetException e) {
                throw new RuntimeException(e);
            }
        };
    }
}

可以使用如下:

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 +
                '}';
    }
}
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);
    }
}

输出:

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}

0
投票

使用函数使其可重用。

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

然后使用它:

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

要么,

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

0
投票

值得一提的是:Apache Commons BeanUtils库专门以动态方式访问bean属性。

请参阅BeanPropertyValueEqualsPredicate以获得理解。这只是平等匹配的解决方案。

© www.soinside.com 2019 - 2024. All rights reserved.