私は次のコードを持っています:
Stream<String> lines = reader.lines();
最初の文字列が"email"
と等しい場合、ストリームから最初の文字列を削除します。ストリームの他の文字列の場合、このチェックは必要ありません。
どうすればそれを達成できますか?
確かに、それをリストに変換してから、オールドスクールforループを使用できますが、さらにもう一度ストリーミングする必要があります。
リーダーは、行のストリームを作成した後は不特定の状態になりますが、実行する前は明確に定義された状態です。
だからできる
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
と同じではないことに注意してください。これは、一致する場合に複数の行をドロップします。
リストがなく、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
で「外部変数」を使用するのは好きではありません。これは、関数型プログラミングのパラダイムでは副作用が悪い習慣だからです。より良いオプションを歓迎します。
@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);
ファイルの各行の状態を確認しないように、最初の行を個別に読み取って確認し、条件を確認せずに残りの行でパイプラインを実行します。
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());
これは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);
インデックスに基づいて要素をフィルタリングするには、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]
}
このアプローチでは、最初の要素だけでなく、任意のインデックスで要素をフィルタリングできます。
これを試して:
MutableBoolean isFirst = MutableBoolean.of(true);
lines..dropWhile(e -> isFirst.getAndSet(false) && "email".equals(e))
このスニペット からインスピレーションを得て、もう少し複雑です。
インデックスを表す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();
}
}