İlk (sıfır dizinli) öğeyi akıştan koşullu olarak kaldır


10

Aşağıdaki kod var:

Stream<String> lines = reader.lines();

Yumruk dizesi eşitse Akıştan "email"ilk dizeyi kaldırmak istiyorum. Akıştaki diğer dizeler için bu kontrole ihtiyacım yok.

Nasıl başarabilirim?

PS

Tabii listeye dönüştürebilirim, sonra eski okulu döngü için kullanabilirim, ancak tekrar akışa ihtiyacım var.


3
Ve ikinci eleman "e-posta" ise onu bırakmak istemiyor musunuz?
michalk

@michalk haklısın
gstackoverflow

Hmm ... skip(long n)ilk nelemanları atlıyor , ama bir şekilde koşullandırabilir misin bilmiyorum ...
deHaar

4
@gstackoverflow Eğer akıştaki ilk iki dizenin csv dosyasının başlığı hakkında konuşuyorsanız bence ne "e-posta" eşit olması olası değilse, kullanabilirsinizstream.dropWhile(s -> s.equals("email"));
Eritrean

1
@Eritrean sadece yayınlayacaktır;)
YCF_L

Yanıtlar:


6

Okuyucu, ondan bir satır akışı oluşturduktan sonra belirtilmemiş bir durumda olsa da, bunu yapmadan önce iyi tanımlanmış bir durumdadır.

Böylece yapabilirsin

String firstLine = reader.readLine();
Stream<String> lines = reader.lines();
if(firstLine != null && !"email".equals(firstLine))
    lines = Stream.concat(Stream.of(firstLine), lines);

Bence en temiz çözüm. Bunun, Java 9'larla aynı olmadığını ve dropWhileeşleşmeleri durumunda birden fazla satır düşeceğini unutmayın.


Kabul et - bu çözüm daha iyi ama dropWhile - ikinci sırada. Ne yazık ki yazar bu fikri ayrı bir cevap olarak göndermemeye karar verdi
gstackoverflow

3

Listeye sahip olamıyorsanız ve listeyi yalnızca a Streamile yapmanız gerekiyorsa, bunu bir değişkenle yapabilirsiniz.

Mesele şu ki, bir değişkeni yalnızca "son" veya "etkin bir şekilde son" ise kullanabilirsiniz, böylece değişmez bir boole kullanamazsınız. Yine de aşağıdakilerle yapabilirsiniz AtomicBoolean:

Stream<String> stream  = Arrays.asList("test", "email", "foo").stream();

AtomicBoolean first = new AtomicBoolean(true);
stream.filter(s -> {
    if (first.compareAndSet(true, false)) {
        return !s.equals("email");
    }
    return true;
})
// Then here, do whatever you need
.forEach(System.out::println);

Not: StreamYan etkiler fonksiyonel programlama paradigmasında kötü bir uygulama olduğu için "dış değişkenler" i kullanmayı sevmiyorum . Daha iyi seçenekler açıktır.


kullanmak compareAndSetaynı zamanda bayrağı ayarlamak ve almak için çok zarif bir yol, güzel.
f1sh

Yapabilir miyiz stream.filter(!first.compareAndSet(true, false) || !s.equals("email"))gereği tartışmalıdır bile.
Joop Eggen

1
predicatevatansız olmalı
ZhekaKozlov

3
AtomicBooleanBurada kullanımı paralel akışları sağlamak için ise ( compareAndSetönerilerin kullanımı gibi ) bu tamamen yanlıştır, çünkü sadece akış sıralı olduğunda çalışacaktır. stream.sequential().filter(...)Ancak tekniği kullanırsanız işe yarayacağına katılıyorum.
daniel

3
@daniel haklısınız, bu yaklaşım paralel işlemeyi desteklemezken bir iplik güvenli yapının (her eleman için) masraflarını ödüyor. Durum bilgisi olan filtrelerle bu kadar çok örneğin bu sınıfları kullanmasının nedeni, iş parçacığı güvenliği olmadan eşdeğer olmaması ve insanların yanıtlarında özel bir sınıf oluşturmanın karmaşıklığından kaçınmalarıdır ...
Holger

1

Dosyanın her satırındaki durumu kontrol etmekten kaçınmak için, ilk satırı ayrı ayrı okuyup kontrol ettim, ardından durumu kontrol etmeden boru hattını diğer satırlarda çalıştırırım:

String first = reader.readLine();
Stream<String> firstLines = Optional.of(first)
        .filter(s -> !"email".equals(s))
        .map(s -> Stream.of(s))
        .orElseGet(() -> Stream.empty());

Stream<String> lines = Stream.concat(firstLines, reader.lines());

Java 9+ üzerinde daha basit:

Stream<String> firstLines = Optional.of(first)
        .filter(s -> !"email".equals(s))
        .stream();

Stream<String> lines = Stream.concat(firstLines, reader.lines());

4
Java 9 daha da basit:Stream.ofNullable(first).filter(not("email"::equals))
Holger

1

@Arnouds yanıtı doğru. İlk satır için bir akış oluşturabilir ve ardından aşağıdaki gibi karşılaştırabilirsiniz,

Stream<String> firstLineStream = reader.lines().limit(1).filter(line -> !line.startsWith("email"));;
Stream<String> remainingLinesStream = reader.lines().skip(1);
Stream.concat(firstLineStream, remainingLinesStream);

bir filtre kullansanız bile, her satır için hesaplanır. Çok büyük dosya olması durumunda performansa neden olabilir. Limit olması durumunda, sadece bir kez karşılaştırılır.
Code_Mode

Bir okuyucu için çalışmamanın yanı sıra, limit(0)mutlaka olmalı limit(1)
Holger

Tanıklıktan sonra cevabı 0 sınırına kadar düzenledim.
Code_Mode

1
Onu değiştirdiğini görüyorum, ama bir .limit(0).filter(line -> !line.startsWith("email"))anlamı yok. Yüklem asla değerlendirilmez. Akışları yeniden üretebilen bir akış kaynağı limit(1)için, birinci ve skip(1)ikinci akışların birleşimi doğru olacaktır. Okuyucu gibi bir akış kaynağı için ne çalışır ne limit(1)de limit(0)çalışır. Hangi hattın koşulsuz yutulduğunu değiştirdiniz. İstediğiniz şeyi yapan bir kombinasyon bulsanız bile, bu belirtilmemiş, uygulamaya bağlı davranış temelinde olacaktır.
Holger

0

Öğeleri dizinlerine göre filtrelemek için, aşağıdakileri AtomicIntegerişlerken dizini depolamak ve artırmak için kullanabilirsiniz Stream:

private static void filter(Stream<String> stream) {
  AtomicInteger index = new AtomicInteger();
  List<String> result = stream
      .filter(el -> {
        int i = index.getAndIncrement();
        return i > 0 || (i == 0 && !"email".equals(el));
      })
      .collect(toList());
  System.out.println(result);
}

public static void main(String[] args) {
  filter(Stream.of("email", "test1", "test2", "test3")); 
  //[test1, test2, test3]

  filter(Stream.of("test1", "email", "test2", "test3")); 
  //[test1, email, test2, test3]

  filter(Stream.of("test1", "test2", "test3")); 
  //[test1, test2, test3]
}

Bu yaklaşım, yalnızca birincisine değil, herhangi bir dizindeki öğelerin filtrelenmesine izin verir.


0

Biraz daha kıvrımlı, bu pasajdan ilham alıyor .

Stream<Integer>Dizinleri temsil edecek bir tane oluşturabilir veStream<String> oluşturmak için birStream<Pair<String, Integer>>

Ardından dizini kullanarak filtreleyin ve bir Stream<String>

public static void main(String[] args) {
    Stream<String> s = reader.lines();
    Stream<Integer> indexes = Stream.iterate(0, i -> i + 1);

    zip(s, indexes)
        .filter(pair -> !(pair.getKey().equals("email") && pair.getValue() == 0))
        .map(Pair::getKey)
        .forEach(System.out::println);
}

private static Stream<Pair<String,Integer>> zip(Stream<String> stringStream, Stream<Integer> indexesStream){
    Iterable<Pair<String,Integer>> iterable = () -> new ZippedWithIndexIterator(stringStream.iterator(), indexesStream.iterator());
    return StreamSupport.stream(iterable.spliterator(), false);
}

static class ZippedWithIndexIterator implements Iterator<Pair<String, Integer>> {
    private final Iterator<String> stringIterator;
    private final Iterator<Integer> integerIterator;

    ZippedWithIndexIterator(Iterator<String> stringIterator, Iterator<Integer> integerIterator) {
        this.stringIterator = stringIterator;
        this.integerIterator = integerIterator;
    }
    @Override
    public Pair<String, Integer> next() {
        return new Pair<>(stringIterator.next(), integerIterator.next());
    }
    @Override
    public boolean hasNext() {
        return stringIterator.hasNext() && integerIterator.hasNext();
    }
}

0

İşte ile örnek Collectors.reducing. Ama sonunda yine de bir liste oluşturur.

Stream<String> lines = Arrays.asList("email", "aaa", "bbb", "ccc")
        .stream();

List reduceList = (List) lines
        .collect(Collectors.reducing( new ArrayList<String>(), (a, v) -> {
                    List list = (List) a;
                    if (!(list.isEmpty() && v.equals("email"))) {
                        list.add(v);
                    }
                    return a;
                }));

reduceList.forEach(System.out::println);

0

Bunu dene:

MutableBoolean isFirst = MutableBoolean.of(true);
lines..dropWhile(e -> isFirst.getAndSet(false) && "email".equals(e))
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.