Java Stream:过滤多个范围


9

我正在尝试过滤资源并基于字段排除一些元素。要排除,我有一个集合(其中包含一个需要排除的ID)和一个列表(其中包含多个范围的需要排除的ID)。我写了下面的逻辑,但对第二个过滤器逻辑不满意。有没有更好的方法可以用Java 8做到这一点?对于包含范围,我也需要这样做。

Set<String> extensionsToExclude = new HashSet<>(Arrays.asList("20","25","60","900"));
List<String> rangesToExclude = new ArrayList<>(Arrays.asList("1-10","20-25","50-70","1000-1000000"));
return directoryRecords.stream()
        .filter((directoryRecord) -> !extensionsToExclude.contains(directoryRecord.getExtensionNumber()))
        .filter((directoryRecord -> {
            Boolean include = true;
            for(String s : rangesToExclude) {
                String [] rangeArray = s.split("-");
                Integer extension = Integer.parseInt(directoryRecord.getExtensionNumber());
                if(extension <= Integer.parseInt(rangeArray[0]) && extension >= Integer.parseInt(rangeArray[1])) {
                    include = false;
                }
            }
            return include;
        }))
        .collect(Collectors.toList());

谢谢 :)


3
Boolean仅在需要boolean值时不要使用对象。尽管在这里,变量include完全过时了。当唯一可能的变化是从truefalse,可以替换include = false;return false;最后的结果已经被确定。然后,return include;可以用替换末尾的,return true;并删除变量声明。而且,由于directoryRecord循环中永远不会发生变化,因此您可以Integer extension = Integer.parseInt(directoryRecord.getExtensionNumber());在循环之前移动(并更改Integerint)。
霍尔格

Answers:


9

我会用一个自定义Range类来做,就像:

class Range {
    private long start;
    private long end;

    Range(String start, String end) {
        this.start = Long.parseLong(start);
        this.end = Long.parseLong(end);
    }

    Range(String range) {
        this(range.split("-")[0], range.split("-")[1]);
    }

    boolean inRange(long n) {
        returns start <= n && n <= end;
    }
}

这将使这样的事情成为可能:

List<Range> ranges = rangesToExclude.stream()
                     .map(Range::new).collect(Collectors.toList());
return directoryRecords.stream()
        .filter((directoryRecord) -> !extensionsToExclude
                                    .contains(directoryRecord.getExtensionNumber()))
        .filter(directoryRecord -> ranges.stream()
                                    .noneMatch(r -> r.isInRange(directoryRecord)))
        .collect(Collectors.toList());

我个人发现您的第一个过滤器足够好,可以按原样保留。


2
它不应该是noneMatch当我们谈论rangesToExclude?而且我想,可能会有一个更优雅的解决方案TreeSet<Range>……
Holger

的确,我一定很困。
ernest_k

@ernest_k谢谢您的解决方案。我觉得它真的很优雅。
Yadvendra Rathore

4

我建议类似ernest_k的答案Range

但是在这种方法中,您可以同时使用两个集合来创建List<Range>"20"可以将其视为"20-20"),也可以将过滤条件更改为对使用否定anyMatch

List<Range> ranges = Stream.concat(extensionsToExclude.stream(), rangesToExclude.stream())
        .map(Range::creatRange).collect(Collectors.toList());

return directoryRecords.stream()
        .filter(directoryRecord -> !ranges.stream()
                .anyMatch(r -> r.isInRange(
                        Integer.parseInt(directoryRecord.getExtensionNumber()))
                ))
        .collect(Collectors.toList());
class Range {
    private int start;
    private int end;

    Range(String start, String end) {
        this.start = Integer.parseInt(start);
        this.end = Integer.parseInt(end);
    }

    static Range creatRange(String range) {
        if (range.contains("-")) {
            return new Range(range.split("-")[0], range.split("-")[1]);
        }
        return new Range(range, range);
    }

    boolean isInRange(int n) {
        return start <= n && n <= end;
    }
}

更新

List<Range> ranges可以更改创建,以从中删除Set<String> extensionsToExclude在中创建的点List<String> rangesToExclud。这样就不会创建不必要的范围。

List<Range> ranges = rangesToExclude.stream().map(Range::creatRange)
        .collect(Collectors.toCollection(ArrayList::new));
extensionsToExclude.stream()
        .filter(v -> !ranges.stream()
                .anyMatch(r -> r.isInRange(Integer.parseInt(v))))
        .map(Range::creatRange)
        .forEach(ranges::add);

0

如果范围条件为true,则可以尽早休息,而不是等待所有条目都被求值。

if(extension >= Integer.parseInt(rangeArray[0]) && extension <= Integer.parseInt(rangeArray[1])) {
                    return true;
                }

否则,在for循环之后返回false。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.