Подтвердить что ты не робот

Разница между first() и take (1)

Я пытаюсь понять детали RxJava.

Интуитивно я ожидал, что first() и take(1) будут равны и будут делать то же самое. Однако путем копания в исходном коде first() определяется как take(1).single().

Что такое single() здесь хорошо? Не позволяет take(1) гарантировать вывод одного элемента?

4b9b3361

Ответ 1

Разница заключается в том, что take(1) будет передавать 0..1 элементы из восходящего потока, тогда как first будет ретранслировать самый первый элемент или испускать ошибку (исключение NoSuchElementException), если восходящий поток пуст. Ни один из них не блокирует.

Истина first == take(1).single(), где take(1) ограничивает количество элементов восходящего потока до 1 и single(), гарантирует, что восходящий поток не пуст.

Этот пример печатает только "Готово"

Observable.empty().take(1)
.subscribe(System.out::println, Throwable::printStackTrace, 
    () -> System.out.println("Done"));

В этом примере печатается "1", а затем "Готово" :

Observable.just(1).take(1)
.subscribe(System.out::println, Throwable::printStackTrace, 
    () -> System.out.println("Done"));

В этом примере также отображается "1", а затем "Готово" :

Observable.just(1, 2, 3).take(1)
.subscribe(System.out::println, Throwable::printStackTrace, 
    () -> System.out.println("Done"));

В этом примере не выполняется NoSuchElementException

Observable.empty().first()
.subscribe(System.out::println, Throwable::printStackTrace, 
    () -> System.out.println("Done"));

В этом примере снова напечатано "1", а затем "Готово" :

Observable.just(1).first()
.subscribe(System.out::println, Throwable::printStackTrace, 
    () -> System.out.println("Done"));

В этом примере снова напечатано "1", а затем "Готово" :

Observable.just(1, 2, 3).first()
.subscribe(System.out::println, Throwable::printStackTrace, 
    () -> System.out.println("Done"));

В этом примере печатается стек trace NoSuchElementException, потому что источник содержит слишком мало элементов:

Observable.empty().single()
.subscribe(System.out::println, Throwable::printStackTrace, 
    () -> System.out.println("Done"));

В этом примере печатается стек IllegalArgumentException, поскольку источник содержит слишком много элементов:

Observable.just(1, 2, 3).single()
.subscribe(System.out::println, Throwable::printStackTrace, 
    () -> System.out.println("Done"));

Ответ 2

Разница в реализации определяется разницей в семантике. Не зная языка и его внутренней работы, рассмотрите, что подразумевают эти два метода.

first() подразумевает, что он вернет ровно один элемент. Первый элемент в коллекции.

take(x) подразумевает, что он вернет коллекцию элементов. Первые элементы x в коллекции.

Различные семантики требуют разных имен. И разные возвращаемые значения требуют различных технических реализаций.

Также возможно (опять же, не глядя под капот, чтобы получить технический ответ), что две реализации могут обрабатывать условия ошибки по-разному. Я бы лично ожидал, что first() выдаст исключение для пустой коллекции, так как нет первого элемента. Тем не менее, я могу разумно ожидать, что take(x) вернет коллекцию, меньшую размера x, без ошибок, если исходная коллекция имеет меньше элементов x. Это может привести к возврату, без ошибок, пустой коллекции всякий раз, когда предоставляется пустая коллекция.

Кроме того, в качестве технического аспекта, что-то вроде take(x), скорее всего, не приведет к немедленному итерации базовой коллекции, а скорее отложит это, пока что-то не повторит его результат. first(), однако, необходимо выполнить итерацию и материализацию первого элемента базовой коллекции.