2017-04-21 1 views
2

스트림 내에서 필터를 걸지 만 우선 순위를 사용하는 옵션을 찾고 있습니다. 다음자바에서 스트림 필터 함수의 우선 순위 지정

의사 코드 : 결과의 목록은 첫 번째 기준으로 필터링해야한다

results.stream().filter(prio1).ifNotFound(filter(prio2)).collect(toList()) 

"prio1"라는 존재하지 않는 경우 일치하는 두 번째 필터는 필터를 사용해 적용되어야한다 발견 prio2이라는 두 번째 기준에 따라 결과가 수집됩니다.

스트림을 사용하여 Java 8에서 어떻게 구현합니까?

나는 한 줄짜리 줄을 찾고 있습니다. 당신이 정말로 한 라이너가 다음 다음 작업을 수행 할 수 있습니다 원하는 경우

final List<Foo> foo; 
if (results.stream().anyMatch(prio1)) { 
    foo = results.stream().filter(prio1).collect(Collectors.toList()); 
} else { 
    foo = results.stream().filter(prio2).collect(Collectors.toList()); 
} 

하지만, 두 번 목록을 스트리밍 주위에 얻을 수있는 방법은 없습니다 :

+0

'ifNotFound'로 무엇을 의미하는지 명확하지 않습니다. –

+0

두 번째 필터 prio2가 적용되지 않을 조건을 말할 수 있습니까? – MigSena

+0

모든 항목은 prio1과 일치해야하며 일치하는 항목이없는 경우 prio2 – Ckkn

답변

3

당신은 두 번 결과를 stream()해야합니다,하지만 한 라이너로 다음과 같은 작업을해야합니다 : flakes

results.stream().filter(results.stream().anyMatch(prio1) ? prio1 : prio2).collect(Collectors.toList()); 

(신용 첫 번째는 비슷한 전략을 사용하여 여러 라이너를 게시.)

편집 : 몇 가지 뛰어난 새로운 답변 이후 빛에왔다, 나는이 다중 스트림의 짧은 방어를 제공 할 것이라고 생각/anyMatch 전략 만들고 이 스레드의 어떤 다른 부분 g 참조가 :

  • As pointed out by eckes, anyMatch 조기 따라서 최소한의 시간 (특히 prio1 일치 할 가능성이있는 경우에) 추가 스트림을 읽는 소요되는 반환하도록 최적화되어 있습니다. 실제로 anyMatch은 대체 스트림 (prio2)의 전체 스트림을 읽으므로 평균 실행에는 1 분의 1 목록 길이 만 반복합니다.

  • 메서드를 사용하면 모든 경우에 맵과 두 개의 List가 생성되지만 위의 방법은 최대 하나의 List 만 생성합니다. 여기서 메모리 오버 헤드의 차이는 results의 크기가 커질수록 상당히 중요해질 것입니다. 그룹화는 전체 스트림에 대해 수행되므로 첫 번째 요소가 prio1을 전달하더라도 모든 요소를 ​​prio1.or(prio2)에 대해 확인한 다음에 대해 prio1을 다시 한 번 확인해야합니다.

  • groupingByprio1prio2이 상호 배타적이지 않은 경우를 설명하지 않습니다. prio2.test(e)e에 대해 true을 반환 할 수있는 경우 prio1을 전달하면 이러한 요소는 대체 prio2 목록 내에 누락됩니다. anyMatch과 한 번에 하나의 필터를 사용하면이 문제를 피할 수 있습니다.

  • 위의 방법의 줄 길이와 복잡성은 나에게 훨씬 관리하기 쉬운 것 같습니다. anyMatch를 사용하지만, 오히려 그룹의 항목 결과를 작동하기 전에하지 않는

+0

여러 번 반복하지 않고 하나의 라이너에 쓸 수있는 방법이 있습니까? – Ckkn

+0

내가 ur 대답을 좋아하지만, 어떻게 든 그것을 실행하기 위해 그것을 최적화하려고 할 수 있습니다. – Ckkn

+1

아이디어에 .. 노력하고 있어요. 그래서 잠시 멈춰요. – gyre

2

그냥 조건을 확인하십시오. 나는 if/else 버전이보다 깔끔하고 유지 보수가 쉽다고 주장한다.

final List<Foo> foo = results.stream() 
    .filter(results.stream().anyMatch(prio1)? prio1 : prio2) 
    .collect(Collectors.toList()); 
+0

에 모든 항목이 다시 일치해야합니다. 그렇지 않으면 else – Ckkn

+3

속성을 조건으로 매핑하여 한 줄로 만들 수 있지만 나는 하나의 라이너를 가짐으로써 위의 조건부 (no : anyMatch는 일찍 돌아가고 prio1이 가능한 경우에 좋음)에 비해 더 나아진다고 생각하지 않는다. – eckes

+0

은 하나의 라이너를 반복하지 않고? – Ckkn

4

그냥 다른 접근 방식. 당신이 "하나 라이너"를 가질 수 있도록

Optional.of(results.stream() 
        .filter(prio1.or(prio2)) 
        .collect(Collectors.groupingBy(prio1::test))) 
     .map(map -> map.getOrDefault(true, map.get(false))) 
     .ifPresent(System.out::println); 

나는 (더 읽기 얻을 수 있도록 단지, 그것을 포맷) Optional을 사용했다. ifPresent 대신 orElseGet(Collections::emptyList)을 사용하고 결과를 List<String>에 저장할 수도 있습니다.

groupingBy

truefalse으로 항목 -matching 나머지 prio2prio1prio2 여과 항목의 모든 항목을 prio1 -matching 둔다. true에 항목이없는 경우 prio2 필터링 된 항목이 기본값으로 반환됩니다. prio1 또는 prio2 - 일치하는 결과가 없으면 아무 일도 발생하지 않습니다.

Map을 직접 반환하는 경우 필터가 상호 배타적 인 경우 모두 에 일치하는 항목이 모두 prio2 일뿐입니다.

+1

오, 이거 멋지다! 'groupingBy'의 영리한 사용. 새로운 것을 가르쳐 주셔서 고맙습니다 :) – gyre

+1

내 해킹보다 훨씬 좋은, 아주 좋습니다. 너무 나쁘면'partitioningBy()'를 사용할 수 없다. 'map()'함수는 약간 불필요하게 영리합니다. '.map (map -> map.getOrDefault (true, map.get (false)))'는 함수와 성능면에서 동등하며 약간 더 길고 훨씬 더 읽기 쉬운 IMO입니다. – shmosel

+0

@shmosel 당신은 또한'partitioningBy'를 사용할 수 있습니다. 그러나 당신은 빈 (empty)이라는 것을 체크 할 필요가 있습니다. 당신은 맞습니다 ...'getOrDefault'는 여기서 더 읽기 쉽습니다 ... 그것을 바꿀 것입니다 ... – Roland