조건부 스트림에서 첫 번째 (0 인덱스 포함) 요소 제거


10

다음 코드가 있습니다.

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

fist string이 같으면 "email"Stream에서 첫 번째 문자열을 제거하고 싶습니다. 스트림의 다른 문자열의 경우이 검사가 필요하지 않습니다.

어떻게 아파요?

추신

물론 목록으로 변환 한 다음 구식 학교 for 루프를 사용할 수 있지만 다시 스트림이 필요합니다.


3
그리고 두 번째 요소가 "이메일"이라면 삭제하지 않겠습니까?
michalk

@ michalk 당신이 맞습니다
gstackoverflow

흠 ... 그것은 skip(long n)첫 번째 n요소 를 건너 뛰는 것이지만, 당신이 어떻게 든 그것을 조절할 수 있는지 모르겠습니다 ...
deHaar

4
@gstackoverflow 스트림의 처음 두 문자열이 "이메일"과 같지 않을 경우 csv 파일의 헤더에 대해 이야기 할 때 생각하는 것과 동일하게 사용할 수 있습니다.stream.dropWhile(s -> s.equals("email"));
Eritrean

1
그것은 단지를 게시 할 예정입니다 @Eritrean)
YCF_L

답변:


6

행 스트림을 구성한 후에는 독자가 지정되지 않은 상태가되지만 행을 작성하기 전에 잘 정의 된 상태입니다.

그래서 당신은 할 수 있습니다

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일치하는 경우 둘 이상의 행이 삭제됩니다.


동의-이 솔루션은 더 좋지만 드롭하는 동안 두 번째입니다. 불행하게도 저자는 분리 된 답변으로이 아이디어를 게시하지 않기로 결정
gstackoverflow

3

목록을 가질 수없고로만 수행해야하는 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함수 프로그래밍 패러다임에서 부작용이 나쁜 습관이기 때문에 "외부 변수"를 사용하는 것을 좋아하지 않습니다 . 더 나은 옵션을 환영합니다.


를 사용하면 compareAndSet동시에 플래그를 설정하고 얻는 매우 우아한 방법입니다.
f1sh

stream.filter(!first.compareAndSet(true, false) || !s.equals("email"))논쟁의 여지 가 있지만 할 수 있습니다.
Joop Eggen

1
predicate상태 비 저장 상태 여야 함
ZhekaKozlov

3
AtomicBoolean여기서 사용하는 것이 병렬 스트림을 제공하는 compareAndSet것이라면 (시도 의 사용으로 ) 스트림이 순차적 인 경우에만 작동하므로 완전히 잘못되었습니다. 그러나 당신이 기술을 함께 사용한다면 stream.sequential().filter(...)나는 그것이 효과가있을 것이라고 동의한다.
다니엘

3
@daniel 당신이 옳습니다.이 접근법은 병렬 처리를 지원하지 않으면 서 모든 요소에 대해 스레드 안전 구문의 비용을 지불합니다. 스테이트 풀 필터가있는 많은 예제가 이러한 클래스를 사용하는 이유는 스레드 안전성이없는 동등한 요소가없고 사람들이 응답에 전용 클래스를 작성하는 복잡성을 피하기 때문입니다.
Holger

1

파일의 각 줄에서 조건을 확인하지 않으려면 첫 번째 줄을 개별적으로 읽고 확인한 다음 조건을 확인하지 않고 나머지 줄에서 파이프 라인을 실행하면됩니다.

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

4
Java 9는 훨씬 더 간단합니다.Stream.ofNullable(first).filter(not("email"::equals))
Holger

1

@Arnouds 답변이 정확합니다. 첫 번째 줄에 대해 하나의 스트림을 만든 다음 아래와 같이 비교할 수 있습니다.

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

필터를 사용하더라도 각 라인에 대해 계산됩니다. 대용량 파일 인 경우 성능이 저하 될 수 있습니다. 제한이있는 경우 한 번만 비교됩니다.
Code_Mode

독자를 위해 일하지 않는 것 외에도, limit(0)반드시 limit(1)
Holger

testimg 후 0을 제한하도록 답변을 편집했습니다.
Code_Mode

1
나는 당신이 그것을 변경했지만 .limit(0).filter(line -> !line.startsWith("email"))말이되지 않는 것을 참조하십시오 . 술어는 평가되지 않습니다. 스트림을 재생할 수있는 스트림 소스의 limit(1)경우 첫 번째와 skip(1)두 번째 의 조합 이 정확합니다. 리더와 같은 스트림 소스의 경우 작동 하지 limit(1)않습니다 limit(0). 무조건 삼키는 라인을 변경했습니다. 원하는 작업을 수행하는 조합을 찾더라도 지정되지 않은 구현 종속 동작을 기반으로합니다.
Holger

0

색인을 기준으로 요소를 필터링하려면 다음 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

이 스 니펫 에서 약간의 영감을 얻은 약간 더 복잡 합니다.

당신은 만들 수 있습니다 Stream<Integer>인덱스를 나타냅니다 그와 함께 "압축" Stream<String>만들려면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

의 예는 다음과 같습니다 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

이 시도:

MutableBoolean isFirst = MutableBoolean.of(true);
lines..dropWhile(e -> isFirst.getAndSet(false) && "email".equals(e))
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.