Stream Java: filtro con più intervalli


9

Sto cercando di filtrare una risorsa ed escludere alcuni elementi in base a un campo. Per escludere ho un set (che contiene un ID che deve essere escluso) e un elenco (contiene più intervalli di ID che devono essere esclusi). Ho scritto la logica di seguito e non sono soddisfatto della seconda logica di filtro. Esiste un modo migliore per farlo con Java 8? Devo fare lo stesso anche per includere le gamme.

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());

Grazie :)


3
Non usare Booleanoggetti quando hai solo bisogno di un booleanvalore. Sebbene qui, la variabile includesia completamente obsoleta. Quando l'unica modifica possibile è da truea false, è possibile sostituirla include = false;con return false;poiché il risultato finale è già stato determinato. Quindi, return include;alla fine può essere sostituito return true;e la dichiarazione di variabile rimossa. E poiché directoryRecordnon cambia mai nel loop, puoi spostare Integer extension = Integer.parseInt(directoryRecord.getExtensionNumber());il ciclo prima del loop (e passare Integera int).
Holger

Risposte:


9

Lo farei con una Rangelezione personalizzata , qualcosa del tipo:

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;
    }
}

Il che renderà possibile qualcosa del genere:

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());

Personalmente trovo il tuo primo filtro abbastanza buono da essere conservato così com'è.


2
Non dovrebbe essere noneMatchquando stiamo parlando rangesToExclude? E suppongo che potrebbe esserci una soluzione ancora più elegante con un TreeSet<Range>...
Holger

In effetti, avrei dovuto avere sonno.
ernest_k

@ernest_k Grazie per la soluzione. Lo trovo davvero elegante.
Yadvendra Rathore,

4

Suggerirei simile alla risposta di ernest_k con Range.

Ma in questo approccio è possibile utilizzare entrambe le raccolte per creare List<Range>(ciò "20"può essere trattato come "20-20") e modificare la condizione del filtro con cui utilizzare la negazione 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;
    }
}

AGGIORNARE

La creazione di List<Range> rangespuò essere modificata per rimuovere i punti Set<String> extensionsToExcludeche si trovano nell'intervallo creato da List<String> rangesToExclud. Quindi non verranno creati intervalli non necessari.

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

puoi fare una pausa anticipata se la condizione dell'intervallo è vera, piuttosto che attendere che tutte le voci vengano valutate.

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

altrimenti restituisce false dopo il ciclo for.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.