web-dev-qa-db-ja.com

条件付きでストリームから最初の(インデックスが0の)要素を削除する

私は次のコードを持っています:

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

最初の文字列が"email"と等しい場合、ストリームから最初の文字列を削除します。ストリームの他の文字列の場合、このチェックは必要ありません。

どうすればそれを達成できますか?

追伸.

確かに、それをリストに変換してから、オールドスクールforループを使用できますが、さらにもう一度ストリーミングする必要があります。

10
gstackoverflow

リーダーは、行のストリームを作成した後は不特定の状態になりますが、実行する前は明確に定義された状態です。

だからできる

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

これは私の意見では最もクリーンなソリューションです。これは、Java 9のdropWhileと同じではないことに注意してください。これは、一致する場合に複数の行をドロップします。

6
Holger

リストがなく、Streamだけで実行する必要がある場合は、変数を使用して実行できます。

問題は、変数が「最終」または「事実上最終」である場合にのみ変数を使用できるため、リテラルブール値を使用できないことです。あなたはまだ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);

注:Streamで「外部変数」を使用するのは好きではありません。これは、関数型プログラミングのパラダイムでは副作用が悪い習慣だからです。より良いオプションを歓迎します。

3

@Arnoudsの答えは正しいです。最初の行に1つのストリームを作成して、以下のように比較できます。

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

ファイルの各行の状態を確認しないように、最初の行を個別に読み取って確認し、条件を確認せずに残りの行でパイプラインを実行します。

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+:

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

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

これはCollectors.reducingの例です。しかし結局のところ、とにかくリストを作成します。

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
lczapski

インデックスに基づいて要素をフィルタリングするには、AtomicIntegerを使用して、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]
}

このアプローチでは、最初の要素だけでなく、任意のインデックスで要素をフィルタリングできます。

0
Evgeniy Khyst

これを試して:

MutableBoolean isFirst = MutableBoolean.of(true);
lines..dropWhile(e -> isFirst.getAndSet(false) && "email".equals(e))
0
user_3380739

このスニペット からインスピレーションを得て、もう少し複雑です。

インデックスを表すStream<Integer>を作成し、それをStream<String>で「Zip」してStream<Pair<String, Integer>>を作成できます。

次に、インデックスを使用してフィルタリングし、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
Bentaye