简体   繁体   中英

How to optimise this filtering of list in Java

I have the following code:

private List<String> validate(StartValue start, List<String> colors, Entity entity) {
    if (!CollectionUtils.isEmpty(colors)) {
        return colors.stream()
                     .filter(color -> ValidationUtil.getColorfulValues(start, color.getRGBValue()).isEmpty() ||
                                      (!ValidationUtil.getColorfulValues(start, color.getRGBValue()).isEmpty() &&
                                       ValidationUtil.getColorfulValues(start, color.getRGBValue()).contains(entity.getColor())))
                     .collect(Collectors.toList());
    }
    return colors;
}

Here ValidationUtil.getColorfulValues is getting called thrice for each value in the list. Is there a way to optimize the filter so that we can save the value of the call?

If you would have not represented the code as a lambda expression, but as a block statement you would have been able to simplify it further.

.filter(color -> {
    List<String> colourFulValues = ValidationUtil.getColorfulValues(start,
                    color.getRGBValue());
    return colourFulValues.isEmpty() || colourFulValues.contains(entity.getColor())
}

Of course, you could abstract the block as a Predicate of its own depending on its usage. The type of the Predicate would be the type you have chosen to represent the color. Currently, in your question, for example, if it's a java.lang.String , where is the method color.getRGBValue() associated from?

You can use a method reference :

private List<String> validate(StartValue start, List<String> colors, Entity entity) {
    if (!CollectionUtils.isEmpty(colors)) {
        return colors.stream()
                     .filter(this::filter)
                     .collect(Collectors.toList());
    }
    return colors;
}

private boolean filter(String color) {
    var rgbVal = color.getRGBValue();
    var cv = ValidationUtil.getColorfulValues(start, rgbVal);
    boolean empty = cv.isEmpty();
    return empty || (!empty && cv.contains(entity.getColor()));
}

Note that I use var since it is not clear to me what color.getRGBValue() returns and also what ValidationUtil.getColorfulValues() returns.

  • Guessing that CollectionUtils.isEmpty considers null elements non-existing.
  • For the filter condition evaluating ValidationUtil.getColorfulValues(start, color.getRGBValue())) only once requires mapping to it. But one needs to conserve the color too, to collect it in a list. So I introduced a record ColorWithValues . The record class is intended for such kind of uses.
  • || is a short-circuit operator and does not need a negation of its first argument.

So:

private List<String> validate(StartValue start, List<String> colors, Entity entity) {
    record ColorWithValues(String color, Set<String> values) {}
    return colors.stream()
                .filter(Objects::nonNull)
                .map(c ->
                     new ColorWithValues(c,
                         ValidationUtil.getColorfulValues(start, c.getRGBValue())))
                .filter(cv -> cv.values.isEmpty() ||
                              cv.values.contains(entity.getColor()))
                .map(cv -> cv.color)
                .collect(Collectors.toList());
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM