Java Stream: กรองด้วยหลายช่วง


9

ฉันกำลังพยายามกรองทรัพยากรและไม่รวมองค์ประกอบบางอย่างตามเขตข้อมูล หากต้องการยกเว้นฉันมีชุด (ที่มีรหัสที่ต้องยกเว้น) และรายการ (มีรหัสช่วงที่ต้องแยกออกหลายช่วง) ฉันเขียนตรรกะด้านล่างและฉันไม่พอใจกับตรรกะตัวกรองที่สอง มีวิธีที่ดีกว่าที่เราสามารถทำได้ด้วย 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>...
โฮลเกอร์

แน่นอนฉันควรจะง่วงนอน
ernest_k

@ernest_k ขอบคุณสำหรับการแก้ปัญหา ฉันคิดว่ามันสวยงามจริงๆ
Yadvendra Rathore

4

ฉันขอแนะนำให้คล้ายกับernest_k 's 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;
    }
}

UPDATE

สร้าง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

คุณสามารถหยุดพักก่อนกำหนดหากเงื่อนไขช่วงเป็นจริงแทนที่จะรอให้รายการทั้งหมดได้รับการประเมิน

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

มิฉะนั้นจะคืนค่า false หลังจาก for for loop

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.