Перевірте примірник потоку


80

Я маю такий вираз:

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

... де scheduleIntervalContainersмає тип елемента ScheduleContainer:

final List<ScheduleContainer> scheduleIntervalContainers

Чи можна перевірити тип перед фільтром?

Відповіді:


132

Ви можете застосувати інший filter, щоб зберегти лише ScheduleIntervalContainerекземпляри, а додавання a mapзаощадить пізніші трансляції:

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

Або, як зауважив Холгер, ви можете замінити лямбда-вирази посиланнями на методи, якщо вам більше подобається такий стиль:

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

122
Або .filter(ScheduleIntervalContainer.class::isInstance) .map(ScheduleIntervalContainer.class::cast), який би стиль ви не віддали перевагу.
Холгер

Якщо в IDEA та Java8 вищезазначений фрагмент присвоєно List <ScheduleContainer> scheduleIntervalContainers, це все одно спонукає мене відкинути результат до List <ScheduleContainer> scheduleIntervalContainers явно, знаєте чому?
К. Символ

@ K.Symbol Чи намагалися ви призначити a List<ScheduleContainer>чи a List<ScheduleIntervalContainer>? Це має бути останнє.
Еран,

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клас на Java?
Допис Self

@PostSelf Справді, це є і ScheduleIntervalContainerнасправді не буде екземпляром.
Наман,

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

Однак при створенні a Streamдля кожного відповідного елемента може бути передбачено покарання за продуктивність . Будьте обережні, щоб використовувати його на величезних масивах даних. Я дізнався це рішення від @ Tagir Vailev


У цьому підході ви повинні подбати про NullPointerExceptions, оскільки select(A.class)повернеться nullза все, що не є A. Додавання .filter(Objects::nonNull)допомогло б. BTW: Підхід @ Ерана є абсолютно безпечним.
Ларс Генднер,

Вибачте, погано ... JavaDoc of flatMapкаже: "Якщо зіставлений потік нульовий, замість нього використовується порожній потік.". Отже, ваше рішення було правильним, навіть без нульового фільтра.
Ларс Генднер,

3
Все одно (IMO) дивно повернутися, nullколи ти міг повернути порожній потік
Alowaniak
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.