Javaストリーム:複数の範囲でフィルター


9

リソースをフィルタリングし、フィールドに基づいて一部の要素を除外しようとしています。除外するには、セット(除外する必要があるIDを含む)とリスト(除外する必要があるIDの複数の範囲を含む)があります。以下のロジックを作成しましたが、2番目のフィルターロジックに満足できません。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は完全に廃止されています。唯一の可能な変更がからtrueへの場合false、あなたは置き換えることができinclude = false;return false;最終結果が既に決定されているよう。次に、return include;末尾のを置き換えてreturn true;、変数宣言を削除できます。またdirectoryRecord、ループ内では決して変更されないため、ループのInteger extension = Integer.parseInt(directoryRecord.getExtensionNumber());前に移動できます(そしてに変更Integerしますint)。
ホルガー

回答:


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を返します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.