스트림에서 instanceof 확인


80

다음식이 있습니다.

scheduleIntervalContainers.stream()
        .filter(sic -> ((ScheduleIntervalContainer) sic).getStartTime() != ((ScheduleIntervalContainer)sic).getEndTime())
        .collect(Collectors.toList());

... scheduleIntervalContainers요소 유형이 있는 위치 ScheduleContainer:

final List<ScheduleContainer> scheduleIntervalContainers

필터 전에 타입을 확인할 수 있습니까?

답변:


132

인스턴스 filter만 유지하기 위해 다른 것을 적용 할 수 ScheduleIntervalContainer있으며를 추가 map하면 나중에 캐스트를 저장할 수 있습니다.

scheduleIntervalContainers.stream()
    .filter(sc -> sc instanceof ScheduleIntervalContainer)
    .map (sc -> (ScheduleIntervalContainer) sc)
    .filter(sic -> sic.getStartTime() != sic.getEndTime())
    .collect(Collectors.toList());

또는 Holger가 언급했듯이 해당 스타일을 선호하는 경우 람다 식을 메서드 참조로 바꿀 수 있습니다.

scheduleIntervalContainers.stream()
    .filter(ScheduleIntervalContainer.class::isInstance)
    .map (ScheduleIntervalContainer.class::cast)
    .filter(sic -> sic.getStartTime() != sic.getEndTime())
    .collect(Collectors.toList());

123
또는 .filter(ScheduleIntervalContainer.class::isInstance) .map(ScheduleIntervalContainer.class::cast)원하는 스타일.
Holger

IDEA 및 Java8에서 위의 스 니펫이 List <ScheduleContainer> scheduleIntervalContainers에 할당 된 경우에도 결과를 List <ScheduleContainer> scheduleIntervalContainers로 명시 적으로 캐스팅하라는 메시지가 계속 표시됩니다. 이유를 알고 있습니까?
K. Symbol

@ K.Symbol a List<ScheduleContainer>또는 a 에 할당하려고 했습니까 List<ScheduleIntervalContainer>? 후자 여야합니다.
Eran

119

매우 우아한 옵션은 클래스의 메서드 참조를 사용하는 것입니다.

scheduleIntervalContainers
  .stream()
  .filter( ScheduleIntervalContainer.class::isInstance )
  .map( ScheduleIntervalContainer.class::cast )
  .filter( sic -> sic.getStartTime() != sic.getEndTime())
  .collect(Collectors.toList() );

캐스트를 위해 instanceof 및 (ScheduleIntervalContainer)를 사용하는 것과 비교할 때이 스타일에 어떤 이점이 있습니까?
MageWind

@MageWind는 대부분 스타일의 문제입니다. 어떤 사람들은 다른 변수 이름 (람다 매개 변수의 경우)을 도입 할 필요가 없기 때문에 선호하고, 다른 사람들은 약간 더 적은 바이트 코드를 생성하기 때문에 (실제로 관련성이있을만큼 차이가 충분하지 않습니다).
Holger

이것은 정말 멋지다! 그러나 왜 .class필요합니까? 의 isInstance일부 가 아니 Object십니까? 가 Class자바의 클래스는?
Post Self

@PostSelf 사실, 그것은 실제로 ScheduleIntervalContainer인스턴스가 아닐 것입니다.
Naman

15

@ Eran 솔루션 에는 작은 문제가 있습니다 . 둘 다에 클래스 이름을 입력 filter하고 map오류가 발생하기 쉽습니다. 두 위치에서 클래스 이름을 변경하는 것을 잊기 쉽습니다. 개선 된 솔루션은 다음과 같습니다.

private static <T, R> Function<T, Stream<R>> select(Class<R> clazz) {
    return e -> clazz.isInstance(e) ? Stream.of(clazz.cast(e)) : null;
}

scheduleIntervalContainers
  .stream()
  .flatMap(select(ScheduleIntervalContainer.class))
  .filter( sic -> sic.getStartTime() != sic.getEndTime())
  .collect(Collectors.toList());   

그러나 Stream일치하는 모든 요소에 대해 를 생성하면 성능이 저하 될 수 있습니다 . 거대한 데이터 세트에서 사용하도록주의하십시오. @ Tagir Vailev 에서이 솔루션을 배웠습니다.


이 방법에서는 이후, NullPointerExceptions를 알아서해야한다 select(A.class)의지 리턴 null아니다 아무것도 A. 추가 .filter(Objects::nonNull)하면 도움이 될 것입니다. BTW : @Eran의 접근 방식은 null-safe입니다.
Lars Gendner

죄송합니다, 제 잘못입니다. JavaDoc flatMap은 "매핑 된 스트림이 null이면 대신 빈 스트림이 사용됩니다."라고 말합니다. 따라서 널 필터 없이도 솔루션이 정확했습니다.
Lars Gendner

3
아직 (IMO) 돌아 홀수 null당신은 단지 빈 스트림 반환했습니다 수있을 때
Alowaniak
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.