使用Stream API更好地记录排除结果的方法

时间:2017-09-26 07:27:11

标签: logging java-8 java-stream

我想知道有没有办法像这样重写代码

public static void main(String[] args) {
    final List<String> dataCollection = Collections.emptyList();
    final Set<String> someValues = new HashSet<>();
    final Iterator<String> iterator = dataCollection.iterator();
    while (iterator.hasNext()) {
        final String dataItem = iterator.next();
        // imagine some calculations
        String calculatedData = dataItem;
        if (!someValues.contains(calculatedData)) {
            logger.error("Skipped data {} because of ...#1", dataItem);
            iterator.remove();
            continue;
        }

        for (char element : dataItem.toCharArray()) {
            // imagine some other calculations
            if (element > 100) {
                logger.error("Skipped data {} because of ...#2", dataItem);
                iterator.remove();
                break;
            }
        }
    }
}

使用Stream API,以便记录过滤后的元素。 peek()在这种情况下不起作用,因为它要么在过滤前对每个元素执行操作,要么在剩余项目后执行。

到目前为止,我设法通过在lambda中记录来设计它,但它似乎冗长,笨拙并且像副作用一样。我们可以将它包装在某个方法中,但它只会隐藏该代码

public static void main(String[] args) {
    final List<String> dataCollection = Collections.emptyList();
    final Set<String> someValues = new HashSet<>();
    final Iterator<String> iterator = dataCollection.iterator();

    dataCollection.stream()
            .filter(byCondition1(someValues))
            .filter(byCondition2())
            .collect(Collectors.toList());
}

private static Predicate<String> byCondition1(Set<String> someValues) {
    return dataItem -> {
        final boolean remain = someValues.contains(dataItem);
        if (!remain) {
            logger.error("Skipped data {} because of ...#1", dataItem);
        }
        return remain;
    };
}

private static Predicate<String> byCondition2() {
    return dataItem -> {
        for (char element : dataItem.toCharArray()) {
            // imagine some other calculations
            if (element > 100) {
                logger.error("Skipped data {} because of element {}...#2", dataItem, element);
                return false;
            }
        }
        return true;
    };
}

我希望有更好的方法。

2 个答案:

答案 0 :(得分:3)

假设跳过是一种特殊情况,这可能是可行的

List<String> dataCollection = Arrays.asList("FOO", "hello", "VALID", "123", "BAR", "bla");
Set<String> someValues = new HashSet<>(Arrays.asList("FOO", "BAR"));

Predicate<String> firstPredicate  = string -> !someValues.contains(string);
Predicate<String> secondPredicate = string -> string.chars().noneMatch(c -> c>100);

List<String> result;

if(!logger.isLoggable(Level.WARNING)) {
    result = dataCollection.stream()
           .filter(firstPredicate)
           .filter(secondPredicate)
           .collect(Collectors.toList());
}
else {
    Map<Boolean, List<String>> map = dataCollection.stream()
        .collect(Collectors.partitioningBy(firstPredicate));
    if(!map.get(false).isEmpty())
        logger.log(Level.WARNING, "Skipped data {0} because of ...#1", map.get(false));
    map = map.get(true).stream()
        .collect(Collectors.partitioningBy(secondPredicate));
    if(!map.get(false).isEmpty())
        logger.log(Level.WARNING, "Skipped data {0} because of ...#2", map.get(false));
    result = map.get(true);
}

此代码使用标准java.util.logging API,如果禁用了指定级别的日志记录,则不会执行任何与日志记录相关的操作。另一方面,如果启用并遇到要跳过的项目,它将在每个条件下一次报告所有这些项目,而不是使用每个元素的条目充斥日志。使用上面显示的示例数据和默认日志处理程序,打印

Sep 26, 2017 11:00:46 AM LoggingExample main
WARNUNG: Skipped data [FOO, BAR] because of ...#1
Sep 26, 2017 11:00:46 AM LoggingExample main
WARNUNG: Skipped data [hello, bla] because of ...#2

结果为[VALID, 123],是否启用了该级别的日志记录。

答案 1 :(得分:2)

首先,我认为OP中的代码并不算太糟糕。其次,我们可以通过原始代码进行一些改进:

final Predicate<String> loggingFilter = dataItem -> {
    final String calculatedData = dataItem; // imagine some calculations
    if (!someValues.contains(calculatedData)) {
        logger.error("Skipped data {} because of ...#1", dataItem);
        return false;
    }         
    final OptionalInt element = dataItem.chars().filter(ch -> ch > 100).findAny();
    if (element.isPresent()) {
        logger.error("Skipped data {} because of element {}...#2", dataItem, element.getAsInt());
        return false;
    }
    return true;
};

dataCollection.stream().filter(loggingFilter).collect(Collectors.toList());

我不确定它是否是:“啰嗦,尴尬和副作用”给你或其他任何人。对我来说,没有重复的代码或副作用。除了某些语言可能会提供链式操作API以删除if(...); if(...)之外,我们几乎可以使用任何语言。