2016-09-30 7 views
2

내가지도에 어떤 객체를 찾고 있어요 발견했을 때 자바 스트림에 술어를 지정하는 방법 : 나는 두 개 이상의 항목을 찾을 때너무 많은 항목

mapObjects.entrySet().stream().map(map -> map.getValue()).filter(predicateA) 

, 나는 몇 가지를 필터링하는 두 번째 술어를 지정하려면를 추가 속성. 스트림의 한 번의 반복에서이 작업을 수행 할 수있는 방법이 있습니까? 아니면 한 번 반복해야합니까? count > 1 다른 조건자를 두 번 반복해야합니까?

예를 들어 사람 목록이 있다고 가정 해보십시오. 먼저 name=John을 찾고 있습니다. 하나 이상의 존이있을 때 나는 surname=Smith을 찾습니다. 이제는 둘 이상이 있다면 나는 상관하지 않습니다.

+0

Stateful 'Predicate'를 사용할 수는 있지만 병렬'Stream'에서는 작동하지 않습니다. –

+0

나는 이것이 좋은 생각이라고 생각하지 않는다. 술어로 데이터를 색인화 한 다음 중복을 감지해야합니다. 이것은 장기적으로보다 효율적입니다. – sturcotte06

+3

'mapObjects.values ​​(). stream()'으로'mapObjects.entrySet(). stream(). map (map -> map.getValue())를 대체 할 수 있습니다. – VGR

답변

1

인스턴스를 먼저 name으로 필터링 한 다음 surname을 그룹화하여 수행 할 수 있습니다. 완전 일치 (namesurname)가없는 경우 첫 번째 일치를 얻으려면 결과를 LinkedHashMap에 넣고 마지막으로 Map#getOrDefault(key, defaultValue)에 의존하여 전체 일치를 얻으면 그렇지 않으면 첫 번째 항목을 기본값으로 가져옵니다 .

Map<String, Person> map = mapObjects.values().stream() 
    .filter(p -> Objects.equals(p.getName(), name)) 
    .collect(
     Collectors.groupingBy(
      Person::getSurname, 
      LinkedHashMap::new, 
      Collectors.collectingAndThen(Collectors.toList(), list -> list.get(0)) 
     ) 
    ); 

Optional<Person> result = 
    map.isEmpty() ? 
     Optional.absent() : 
     Optional.of(
      map.getOrDefault(surname, map.entrySet().iterator().next().getValue()) 
     ); 

당신이 당신의 결과를 얻기 위해 한 번만 반복하고 당신이 상태 Predicate를 사용하지 않는이 방법.

mapObjects.values().stream() 
    .filter(predicateA) 
    .reduce((acc, obj) -> predicateB.test(obj) ? obj : acc) 
    .ifPresent(doThing); 

불행하게도, 감소가 단락 될 수 없습니다

0

당신은 가능한 두 번째 조건의 우선 순위를 환원 작업을 사용할 수 있습니다. 이것이 중요한 경우, 계속 읽으십시오.


당신은 그것이 결코 전에없는 인수가 기준을 충족하는 경우 true를 반환하려고 predicateB 래핑 클래스를 줄 수 있습니다. 다음은 원자 적 구현이므로 병렬 스트림에서 여전히 작동합니다.

public class ShortCircuitPredicate<T> implements Predicate<T> { 
    private final AtomicBoolean hasBeenTrue; 
    private final Predicate<T> predicate; 
    private ShortCircuitPredicate(Predicate<T> pred) { 
     hasBeenTrue = new AtomicBoolean(false); 
     predicate = pred; 
    } 

    public static <T> ShortCircuitPredicate<T> of(Predicate<T> pred) { 
     return new ShortCircuitPredicate<>(pred); 
    } 

    @Override 
    public boolean test(T t) { 
     return hasBeenTrue.get() 
      ? false 
      : predicate.test(t) && hasBeenTrue.compareAndSet(false, true); 
    } 
} 

당신은 ShortCircuitPredicate.of(predicateB)를 사용하여 predicateB을 포장 할 수 있습니다.

0

이 문제는 스트림 API를 사용하는 데 적합하다고 확신하지는 않지만, 첫 번째 필터와 일치하는 요소 중 하나에 대한 참조를 유지하기 위해 Stream#peek()에 의존하는 것이 좋습니다 :

List<Person> people = ... 
    Person[] holder = new Person[1]; 

    Person result = people.stream() 
      .filter(p -> p.getName().equals("John")) 
      .peek(p -> holder[0] = p) 
      .filter(p -> p.getSurname().equals("Smith")) 
      .findAny() 
      .orElse(holder[0]); 

두 필터가 일치하는 경우 단락이 발생합니다. 반면에 peek()이고 두 번째 필터는 첫 번째 조건 자의 모든 일치에서 실행되어야합니다. findAny()은 빈 옵션을 반환합니다. 결과적으로, 첫 x 째 술어와 일치하는 경우를 제외하고는 항상 소유자가 채워집니다.

나는 신중하게 In Java streams is peek really only for debugging?을 읽는 것이 좋습니다. 그리고 이것이 자신의 구체적인 경우에 적절한 옵션인지 여부에 대해 의견을 나눕니다.