Java Stream: Birden çok aralıklı filtre


9

Bir kaynağı filtrelemek ve bir alana dayalı bazı öğeleri dışlamak çalışıyorum. Hariç tutmak için (hariç tutulması gereken bir kimlik içeren) bir kümem ve bir liste (hariç tutulması gereken kimlikleri birden çok aralık içerir). Aşağıdaki mantığı yazdım ve 2. filtre mantığından memnun değilim. Java 8 ile yapmanın daha iyi bir yolu var mı? Aralıkları dahil etmek için de aynısını yapmam gerekiyor.

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

Teşekkürler :)


3
BooleanSadece bir booleandeğere ihtiyacınız olduğunda nesneleri kullanmayın . Burada olmasına rağmen, değişken includetamamen eski. Mümkün olan tek değişiklik - arasında trueolduğunda false, sonuç zaten belirlendiği için include = false;ile değiştirebilirsiniz return false;. Daha sonra, return include;sondaki ile değiştirilebilir return true;ve değişken bildirimi kaldırılabilir. Beri directoryRecorddöngüde hiç değişmez, hareket edebilir Integer extension = Integer.parseInt(directoryRecord.getExtensionNumber());döngü önce (ve değiştirmek Integeriçin int).
Holger

Yanıtlar:


9

Ben özel bir Rangesınıf, şöyle bir şey ile yaparım :

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

Hangi böyle bir şey mümkün kılacak:

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

Şahsen ilk filtrenizi olduğu gibi korunacak kadar iyi buluyorum.


2
noneMatchKonuşurken olmamalı mıydı rangesToExclude? Ve sanırım, daha şık bir çözüm olabilir TreeSet<Range>
Holger

Gerçekten uykulu olmalıydım.
ernest_k

@ernest_k Çözüm için teşekkür ederim. Gerçekten zarif buluyorum.
Yadvendra Rathore

4

Ben ernest_k 'ın yanıtı benzer öneriyoruz Range.

Ancak bu yaklaşımda, her iki koleksiyonu kullanarak olumsuzlama kullanılacak filtre koşulunu oluşturabilir List<Range>(bu "20"olarak değerlendirilebilir "20-20") ve filtre koşulunu değiştirebilirsiniz 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;
    }
}

GÜNCELLEME

Oluşturma aralığı, oluşturulan aralıktaki List<Range> rangesnoktaları kaldırmak için değiştirilebilir . O zaman gereksiz aralıklar oluşturulmaz.Set<String> extensionsToExcludeList<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

tüm girdilerin değerlendirilmesini beklemek yerine aralık koşulu doğruysa erken bir mola verebilirsiniz.

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

aksi halde for döngüsünden sonra false değerini döndürür.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.