Потік Java: фільтр із кількох діапазонів


9

Я намагаюся фільтрувати ресурс і виключати деякі елементи на основі поля. Щоб виключити, у мене є набір (який містить ідентифікатор, який потрібно виключити) та список (він містить безліч діапазонів ідентифікаторів, які потрібно виключити). Я написав нижченаведену логіку і мене не влаштовує логіка 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 Дякую за рішення. Я вважаю це дійсно елегантним.
Ядвендра Ратхор

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

ви можете зробити перерву на ранньому терміні, якщо умова діапазону відповідає дійсності, а не чекати, коли всі записи будуть оцінені.

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

в іншому випадку просто поверніть false після циклу for.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.